Warum ignoriert awk das Leerzeichen nicht als Trennzeichen?

Warum ignoriert awk das Leerzeichen nicht als Trennzeichen?

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.

Debug-Screenshot

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 $2wenn 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.

verwandte Informationen