Wie entferne ich \ n zwischen den Ausgaben zweier Echobefehle?

13

Ich habe eine Textdatei mit einem Dateinamen in jeder Zeile:

111_c4l5r120.png
123_c4l4r60.png
135_c4l4r180.png
147_c4l3r60.png
15_c4l1r120.png
...

Ich möchte es in diese Form umwandeln:

111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
...

mit diesem Code:

#!/bin/bash
while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" >> output.txt   
   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

aber das Ergebnis ist:

111_c4l5r120.png 
111
123_c4l4r60.png 
123
135_c4l4r180.png 
135
147_c4l3r60.png 
147
15_c4l1r120.png 
15
...

Wie soll ich mein Skript ändern, um die gewünschte Ausgabe zu erhalten?

Ali
quelle
Google findet bessere Ergebnisse, zB dies .
Thomas Dickey

Antworten:

17

Sofern Sie die Shell nicht speziell dafür benötigen, bietet die Antwort von terdon bessere Alternativen.

Da Sie bash(wie im Schebang des Skripts angegeben) verwenden, können Sie die -nOption zum Echo verwenden:

echo -n "${line} " >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

Oder Sie können Shell-Features verwenden, um die Zeile zu verarbeiten, ohne Folgendes zu verwenden cut:

echo "${line} ${line%%_*}" >> output.txt

(beide echoZeilen ersetzen ).

Alternativ können printfSie auch den Trick ausführen, in jeder POSIX-Shell arbeiten und im Allgemeinen besser sein (weitere Informationen finden Sie unter Warum ist printf besser als Echo? ):

printf "%s " "${line}" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

oder

printf "%s %s\n" "${line}" "${line%%_*}" >> output.txt

(Streng genommen in Ebene /bin/sh, echo -nnicht tragbar ist . Da Sie explizit mit bashihm ist OK hier.)

Stephen Kitt
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Terdon
23

Mach so etwas nicht in der Shell! Es ist weitaus komplexer als nötig, fehleranfällig und weitaus langsamer. Es gibt viele Tools, die für eine solche Textmanipulation entwickelt wurden. Zum Beispiel insed (hier unter der Annahme der jüngsten GNU- oder BSD-Implementierungen für -E):

$ sed -E 's/([^_]*).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

Oder für jeden sed :

$ sed 's/\([^_]*\).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

Perl:

$ perl -pe 's/(.+?)_.*/$& $1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

awk:

$ awk -F_ '{print $0,$1}' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
terdon
quelle
1
Externe Dienstprogramme sind jedoch nicht viel besser.
EKons
6
@ ΈρικΚωνσταντόπουλος ja sie sind. Tatsächlich mehrere Größenordnungen schneller. Die Schale ist einfach nicht sehr gut in so etwas. Die Hauptaufgabe einer Shell besteht schließlich darin, externe Dienstprogramme zu starten. Vergleichen Sie die Zeit, die der Ansatz des OP benötigt, mit der Zeit, die eine der hier beschriebenen Lösungen benötigt. Shell-Loops sind sehr, sehr langsam. Wenn Sie überzeugender sein möchten, lesen Sie dies .
Terdon
In Bezug auf die Portabilität, nein. In Sachen Geschwindigkeit ja. Ist @ StéphaneChazelas auch Ihr Pseudonym?
EKons
4
@ ΈρικΚωνσταντόπουλος Θα 'θελα :) Nein, er hat nur zufällig 2 großartige Antworten geschrieben, die für die beiden Kommentarthreads relevant waren. Mit Ausnahme des Perl-Ansatzes, der nur auf etwa 90% der * nix-Maschinen funktioniert, sind alle drei Lösungen portabel und shellunabhängig. Oder, OK, Sie könnten immer sedeine sed 's/\([^_]*\).*/& \1/' filefür zusätzliche Portabilität machen. Ist, können Sie sich verlassen awkund sedist es mehr , als man auf so ziemlich alles andere zählen.
Terdon
2

Hier sind Sie ja:

#!/bin/bash

while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" `echo "$line" | cut -d'_' -f 1` >> output.txt
#   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

Ausgabe:

$ rm -rf output.txt
$ ./test.sh 1.1; cat output.txt
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Putnik
quelle