J'ai écrit un script Shell dans ksh pour convertir un fichier CSV en fichier XML Spreadsheet. Il prend un fichier CSV existant (dont le chemin est une variable dans le script), puis crée un nouveau fichier de sortie .xls. Le script n'a pas de paramètres positionnels. Le nom de fichier du CSV est actuellement codé en dur dans le script.
Je voudrais modifier le script afin qu'il puisse prendre les données CSV d'entrée d'un tube, et que les données de sortie .xls puissent également être redirigées ou redirigées (>) vers un fichier sur la ligne de commande.
Comment y parvient-on?
J'ai du mal à trouver de la documentation sur la façon d'écrire un script Shell pour prendre les entrées d'un tube. Il semble que 'read' ne soit utilisé que pour l'entrée std de kb.
Merci.
Edit: script ci-dessous pour info (maintenant modifié pour prendre l'entrée d'un tuyau via le chat, selon la réponse à la question.
#!/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
Les commandes héritent de leur entrée standard du processus qui les démarre. Dans votre cas, votre script fournit son entrée standard pour chaque commande qu'il exécute. Un exemple de script simple:
#!/bin/bash
cat > foo.txt
Le fait de canaliser des données dans votre script Shell fait que cat
lit ces données, puisque cat
hérite de son entrée standard de votre script.
$ echo "Hello world" | myscript.sh
$ cat foo.txt
Hello world
La commande read
est fournie par le Shell pour lire le texte de l'entrée standard dans une variable Shell si vous n'avez pas d'autre commande pour lire ou traiter l'entrée standard de votre script.
#!/bin/bash
read foo
echo "You entered '$foo'"
$ echo bob | myscript.sh
You entered 'bob'
Il y a un problème ici. Si vous exécutez le script sans vérifier d'abord qu'il y a une entrée sur stdin, il se bloquera jusqu'à ce que quelque chose soit tapé.
Donc, pour contourner ce problème, vous pouvez vérifier qu'il y a d'abord stdin, et sinon, utiliser un argument de ligne de commande à la place s'il est donné.
Créez un script appelé "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
Puis pour tester:
Ajoutons des éléments à un fichier test.txt, puis dirigons la sortie vers notre script.
printf "stuff\nmore stuff\n" > test.txt
cat test.txt | ./testPipe.sh
Production: Data was piped to this script! Line: stuff Line: more stuff
Maintenant, testons si nous ne fournissons aucune entrée:
./testPipe.sh
Production: No input was found on stdin, skipping! No input given!
Maintenant, testons si nous fournissons un nom de fichier valide:
./testPipe.sh test.txt
Production: No input was found on stdin, skipping! Filename specified: test.txt Doing things now..
Et enfin, testons en utilisant un nom de fichier invalide:
./testPipe.sh invalidFile.txt
Production: No input was found on stdin, skipping! No input given!
Explication: Des programmes comme read et cat utiliseront le stdin s'il est disponible dans le shell, sinon ils attendront l'entrée.
Nous remercions Mike de cette page dans sa réponse montrant comment vérifier l'entrée stdin: https://unix.stackexchange.com/questions/33049/check-if-pipe-is-empty-and-run- a-command-on-the-data-if-it-isnt? newreg = fb5b291531dd4100837b12bc1836456f
Si le programme externe (que vous scriptez) prend déjà une entrée de stdin, votre script n'a rien à faire. Par exemple, awk lit depuis stdin, donc un petit script pour compter les mots par ligne:
#!/bin/sh
awk '{print NF}'
Alors
./myscript.sh <<END
one
one two
one two three
END
les sorties
1
2
3