2013-10-16 53 views
6

我已经在ksh中编写了一个shell脚本来将CSV文件转换为Spreadsheet XML文件。它需要一个现有的CSV文件(脚本中的变量路径),然后创建一个新的输出文件.xls。该脚本没有位置参数。 CSV的文件名称目前被硬编码到脚本中。将管道输入到脚本中

我想修改脚本,以便它可以从管道输入CSV数据,以便.xls输出数据也可以通过管道或重定向(>)到命令行上的文件中。

这是如何实现的?

我正在努力寻找关于如何编写shell脚本以从管道获取输入的文档。看起来'read'只用于kb的std输入。

谢谢。

编辑:下面的脚本为信息(现修正通过猫采取输入从管道,按照该问题的答案

#!/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 "${r}p" $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}-${DD}T00: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 
+0

显示我们的脚本... – devnull

回答

20

命令,从启动它们的进程继承其标准输入。在你的情况,脚本提供,它运行一个简单的例子脚本每个命令它的标准输入:

#!/bin/bash 
cat > foo.txt 

管道数据到外壳脚本使得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' 
4

如果外部程序(您脚本)已经采取从标准输入,你的脚本不需要做任何事情。例如,AWK从stdin读取,所以很短的脚本来计算单词每行:

#!/bin/sh 
awk '{print NF}' 

然后

./myscript.sh <<END 
one 
one two 
one two three 
END 

输出

1 
2 
3 
8

这里有一个问题。如果你没有首先检查脚本来运行脚本以确保在stdin上有输入,那么它将挂起直到输入内容。

所以,为了解决这个问题,你可以检查以确保首先有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!

说明: 像read和cat这样的程序将使用stdin(如果它在shell中可用),否则它们将等待输入。

幸得迈克从这个页面在他的回答显示了如何检查标准输入输入:https://unix.stackexchange.com/questions/33049/check-if-pipe-is-empty-and-run-a-command-on-the-data-if-it-isnt?newreg=fb5b291531dd4100837b12bc1836456f