管道输入到脚本中
Posted
技术标签:
【中文标题】管道输入到脚本中【英文标题】:Pipe input into a script 【发布时间】:2013-10-24 21:01:58 【问题描述】:我在 ksh 中编写了一个 shell 脚本来将 CSV 文件转换为电子表格 XML 文件。它需要一个现有的 CSV 文件(其路径是脚本中的一个变量),然后创建一个新的输出文件 .xls。该脚本没有位置参数。 CSV 的文件名当前被硬编码到脚本中。
我想修改脚本,以便它可以从管道获取输入的 CSV 数据,这样 .xls 输出数据也可以通过管道传输或重定向 (>) 到命令行上的文件。
这是如何实现的?
我正在努力寻找有关如何编写 shell 脚本以从管道中获取输入的文档。 'read' 似乎只用于来自 kb 的 std 输入。
谢谢。
编辑:下面的脚本以获取信息(根据问题的答案,现在已修改为通过 cat 从管道获取输入。
#!/bin/ksh
#Script to convert a .csv data to "Spreadsheet ML" XML format - the XML scheme for Excel 2003
#
# Take CSV data as standard input
# Out XLS data as standard output
#
DATE=`date +%Y%m%d`
#define tmp files
INPUT=tmp.csv
IN_FILE=in_file.csv
#take standard input and save as $INPUT (tmp.csv)
cat > $INPUT
#clean input data and save as $IN_FILE (in_file.csv)
grep '.' $INPUT | sed 's/ *,/,/g' | sed 's/, */,/g' > $IN_FILE
#delete original $INPUT file (tmp.csv)
rm $INPUT
#detect the number of columns and rows in the input file
ROWS=`wc -l < $IN_FILE | sed 's/ //g' `
COLS=`awk -F',' 'print NF; exit' $IN_FILE`
#echo "Total columns is $COLS"
#echo "Total rows is $ROWS"
#create start of Excel File
echo "<?xml version=\"1.0\"?>
<?mso-application progid=\"Excel.Sheet\"?>
<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"
xmlns:o=\"urn:schemas-microsoft-com:office:office\"
xmlns:x=\"urn:schemas-microsoft-com:office:excel\"
xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"
xmlns:html=\"http://www.w3.org/TR/REC-html40\">
<DocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">
<Author>Ben Hamilton</Author>
<LastAuthor>Ben Hamilton</LastAuthor>
<Created>$DATE</Created>
<Company>MCC</Company>
<Version>10.2625</Version>
</DocumentProperties>
<ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">
<WindowHeight>6135</WindowHeight>
<WindowWidth>8445</WindowWidth>
<WindowTopX>240</WindowTopX>
<WindowTopY>120</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID=\"Default\" ss:Name=\"Normal\">
<Alignment ss:Vertical=\"Bottom\" />
<Borders />
<Font />
<Interior />
<NumberFormat />
<Protection />
</Style>
<Style ss:ID=\"AcadDate\">
<NumberFormat ss:Format=\"Short Date\"/>
</Style>
</Styles>
<Worksheet ss:Name=\"Sheet 1\">
<Table>
<Column ss:AutoFitWidth=\"1\" />"
#for each row in turn, create the XML elements for row/column
r=1
while (( r <= $ROWS ))
do
echo "<Row>\n"
c=1
while (( c <= $COLS ))
do
DATA=`sed -n "$rp" $IN_FILE | cut -d "," -f $c `
if [[ "$DATA" == [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9][0-9][0-9] ]]; then
DD=`echo $DATA | cut -d "." -f 1`
MM=`echo $DATA | cut -d "." -f 2`
YYYY=`echo $DATA | cut -d "." -f 3`
echo "<Cell ss:StyleID=\"AcadDate\"><Data ss:Type=\"DateTime\">$YYYY-$MM-$DDT00:00:00.000</Data></Cell>"
else
echo "<Cell><Data ss:Type=\"String\">$DATA</Data></Cell>"
fi
(( c+=1 ))
done
echo "</Row>"
(( r+=1 ))
done
echo "</Table>\n</Worksheet>\n</Workbook>"
rm $IN_FILE > /dev/null
exit 0
【问题讨论】:
给我们看脚本... 【参考方案1】:命令从启动它们的进程继承其标准输入。在您的情况下,您的脚本为其运行的每个命令提供其标准输入。一个简单的示例脚本:
#!/bin/bash
cat > foo.txt
将数据导入您的 shell 脚本会导致 cat
读取该数据,因为 cat
从您的脚本继承其标准输入。
$ echo "Hello world" | myscript.sh
$ cat foo.txt
Hello world
read
命令由 shell 提供,用于将标准输入中的文本读取到 shell 变量中,如果您没有其他命令来读取或处理脚本的标准输入。
#!/bin/bash
read foo
echo "You entered '$foo'"
$ echo bob | myscript.sh
You entered 'bob'
【讨论】:
好像不支持多行输入 不,read
只读取一行(即直到第一个换行符)。【参考方案2】:
这里有一个问题。如果您在没有先检查以确保在 stdin 上有输入的情况下运行脚本,那么它将挂起,直到输入某些内容。
因此,为了解决这个问题,您可以先检查以确保有标准输入,如果没有,则使用命令行参数(如果有)。
创建一个名为“testPipe.sh”的脚本
#!/bin/bash
# Check to see if a pipe exists on stdin.
if [ -p /dev/stdin ]; then
echo "Data was piped to this script!"
# If we want to read the input line by line
while IFS= read line; do
echo "Line: $line"
done
# Or if we want to simply grab all the data, we can simply use cat instead
# cat
else
echo "No input was found on stdin, skipping!"
# Checking to ensure a filename was specified and that it exists
if [ -f "$1" ]; then
echo "Filename specified: $1"
echo "Doing things now.."
else
echo "No input given!"
fi
fi
然后进行测试:
让我们在 test.txt 文件中添加一些内容,然后将输出通过管道传输到我们的脚本。
printf "stuff\nmore stuff\n" > test.txt
cat test.txt | ./testPipe.sh
输出:
Data was piped to this script!
Line: stuff
Line: more stuff
现在让我们测试一下是否不提供任何输入:
./testPipe.sh
输出:
No input was found on stdin, skipping!
No input given!
现在让我们测试是否提供了有效的文件名:
./testPipe.sh test.txt
输出:
No input was found on stdin, skipping!
Filename specified: test.txt
Doing things now..
最后,让我们使用无效的文件名进行测试:
./testPipe.sh invalidFile.txt
输出:
No input was found on stdin, skipping!
No input given!
解释: 如果标准输入在 shell 中可用,则 read 和 cat 等程序将使用标准输入,否则它们将等待输入。
Mike 在他的回答中显示了如何检查标准输入输入:https://unix.stackexchange.com/questions/33049/check-if-pipe-is-empty-and-run-a-command-on-the-data-if-it-isnt?newreg=fb5b291531dd4100837b12bc1836456f
【讨论】:
这是一个更好的答案,尤其是在与 csv 文件相关的问题的上下文中,假设管道输入包含多于一行。【参考方案3】:如果外部程序(您正在编写脚本)已经从标准输入获取输入,则您的脚本不需要执行任何操作。例如,awk 从 stdin 读取,所以一个简短的脚本来计算每行的字数:
#!/bin/sh
awk 'print NF'
然后
./myscript.sh <<END
one
one two
one two three
END
输出
1
2
3
【讨论】:
以上是关于管道输入到脚本中的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Perl 将输入通过管道传输到 Java 应用程序?