Ich habe ein Problem mit meinem Skript.
Auftakt Erstens habe ich eine Liste, eine 100-Zeilen-Datei wie diese:
100;TEST ONE
101;TEST TWO
...
200;TEST HUNDRED
Jede Zeile hat zwei Argumente. Die Argumente der ersten Zeile lauten beispielsweise: „645“, „TEST ONE“. Das Semikolon ist also ein Trennzeichen.
Ich muss beide Argumente in zwei Variablen setzen. Nehmen wir an, es sind $id und $name. Für jede Zeile sind die Werte von $id und $name unterschiedlich. Zum Beispiel für die zweite Zeile: $id = „646“ und $name = „TEST TWO“.
Danach muss ich die Beispieldatei nehmen und vordefinierte Schlüsselwörter in $id- und $name-Werte ändern. Die Beispieldatei sieht folgendermaßen aus:
xxx is yyy
Und als Ergebnis möchte ich 100 Dateien mit unterschiedlichem Inhalt haben. Jede Datei muss $id- und $name-Daten aus jeder Zeile enthalten. Und sie muss nach ihrem $name-Wert benannt sein.
Hier ist mein Skript:
#!/bin/bash -x
rm -f output/*
for i in $(cat list)
do
id="$(printf "$i" | awk -F ';' '{print $1}')"
name="$(printf "$i" | awk -F ';' '{print $2}')"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/$name.xml
done
Also versuche ich einfach, meine Listendatei Zeile für Zeile zu lesen. Für jede Zeile erhalte ich zwei Variablen und verwende sie dann, um Schlüsselwörter (xxx und yyy) aus der Beispieldatei zu ersetzen und dann das Ergebnis zu speichern.
Aber etwas ist schiefgelaufen
Als Ergebnis habe ich nur 1 Ausgabedatei. Und das Debuggen sieht schlecht aus.
Hier ist das Debug-Fenster mit nur 2 Zeilen in meiner Listendatei. Ich habe nur eine Ausgabedatei. Der Dateiname ist einfach „TEST“ und enthält eine Zeichenfolge: „101 ist TEST“.
Es werden zwei Dateien erwartet: „TEST EINS“, „TEST ZWEI“ und sie müssen „100 ist TEST EINS“ und „101 ist TEST ZWEI“ enthalten.
Wie Sie sehen, enthält die zweite Variable ein Leerzeichen (z. B. „TEST ONE“). Ich denke, das Problem hängt mit dem Sonderzeichen „Leerzeichen“ zusammen, aber ich weiß nicht, warum. Ich habe den awk-Parameter -F auf „;“ gesetzt, sodass awk nur das Semikolon als Trennzeichen interpretieren darf!
Was habe ich falsch gemacht?
Antwort1
Wenn ich Sie richtig verstehe, können Sie eine while-Schleife und Variablenerweiterung verwenden
while IFS= read -r line; do
id="${line%;*}"
name="${line#*;}"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
Wie von @steeldriver vorgeschlagen, hier ist eine (elegantere) Option:
while IFS=';' read -r id name; do
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
Antwort2
Zitat !!. Das Zitat in dieser Zeile fehlt:
mv output/input.tmp output/$name.xml
Es sollte sein:
mv output/input.tmp output/"$name".xml
um Probleme mit einem Dateinamen mit Leerzeichen zu vermeiden.
Und die Erweiterung $(cat list)
wird durch die Shell aufgeteilt (und globt), was auch zu Leerzeichen führt.
Vielleicht kannst du es zu diesem Skript ändern:
#!/bin/bash -x
rm -f output/*
inputfile=output/input.tmp
while read -r line
do
id=${line%%;*}
name=${line##*;}
cp sample.xml "$inputfile"
sed -i -e "s/xxx/$id/g" "$inputfile"
sed -i -e "s/yyy/$name/g" "$inputfile"
mv "$inputfile" output/"$name".xml; echo
done <list
Antwort3
Der Grund, warum Ihr awk nicht die erwarteten Ergebnisse liefert, liegt an der Art und Weise, wie Sie über die Datei iterieren. Wenn Sie mit iterieren for i in $(cat file)
, iterieren Sie über Wörter (aufgeteilt durch IFS), nicht über Zeilen. Um eine Datei zeilenweise zu lesen, verwenden Sie while read
:
while read -r line; do
...
done < file
Weitere Informationen finden Sie in den folgenden Bash-FAQs:Wie kann ich eine Datei (Datenstrom, Variable) zeilenweise (und/oder feldweise) lesen?
Antwort4
Als alternativer AnsatzSie können diese Aufgabe mit awk erledigenin 1 Prozess statt 4 für jede Zeile. Dies ist vor allem dann von Vorteil, wenn die Liste viele Zeilen enthält, sample.xml jedoch klein ist.
awk -F';' 'FNR==NR{x=x $0 RS; next}
{t=x; gsub(/xxx/,$1,t); gsub(/yyy/,$2,t); f="output/"$2".xml"; printf "%s",t >f; close(f)}
' sample.xml list
# shown with unnecessary linebreaks for clarity, but you can put it all on one line
Wenn die Liste CRLF-Zeilenenden (auch bekannt als DOS- oder Windows-Format) hat, wie in Ihrer Frage kommentiert, und Sie diese nicht (einfach) zuerst entfernen können oder möchten, kann awk das auch verarbeiten; direkt nach dem zweiten {
Einfügen sub(/\r$/,"",$0);
(oder $2
wenn Sie das bevorzugen).
Perl kann dies auch (Perl kann fast alles, was Awk kann), allerdings etwas ausführlicher, und obwohl Perl allgemein verfügbar ist, ist es nicht POSIX wie Awk.