Wie füge ich Zeilen mit Zahlen von X bis Y nach Zeile Z in eine andere Datei ein?

7

Ich habe zwei Dateien erstellt:

echo -e "1\n2\n3\n4\n5" > 123.txt
echo -e "a\nb\nc\nd\ne" > abc.txt

Ich möchte die Datei 123.txtmit folgendem Inhalt erhalten:

1
2
3
b
c
d
4
5

Mit anderen Worten, fügen Sie die Zeilen mit den Nummern 2 bis 4 abc.txtder Datei 123.txtnach der dritten Zeile in die Datei ein .

Ich habe die vielen ähnlichen Fragen hier geprüft, aber keine passende Antwort gefunden. Trotzdem habe ich die Zeilen bekommen:

sed -n '2,4p' abc.txt

und fügen Sie nach der dritten Zeile Text in die Datei ein:

sed -i '3a\mytext' 123.txt

Wie mache ich das mit dem vorherigen Befehl stdout oder / und einem sed/ awksingle Befehl?

Apostel
quelle
Keine elegante Lösung. Kann aber trotzdem als einzelner Befehl ausgeführt werden. head -n +3 123.txt > tmp && cat abc.txt >> tmp && tail -n +2 123.txt >> tmp && mv tmp 123.txt
Ramesh
@Ramesh Ich stimme dir zu. Ihr Befehl wurde nur korrigiert, weil er das falsche Ergebnis liefert. head -3 123.txt > tmp && tail -4 abc.txt | head -3 >> tmp && tail -2 123.txt >> tmp && mv tmp 123.txt
Apostel

Antworten:

6

Wenn Ihr System über die GNU-Version von verfügt sed, können Sie den rBefehl GNU-Erweiterung verwenden :

r filename
    As a GNU extension, this command accepts two addresses.

    Queue the contents of filename to be read and inserted into the
    output stream at the end of the current cycle, or when the next input 
    line is read. Note that if filename cannot be read, it is treated as 
    if it were an empty file, without any error indication.

    As a GNU sed extension, the special value /dev/stdin is supported for 
    the file name, which reads the contents of the standard input. 

Zum Beispiel,

$ sed '3r /dev/stdin' 123.txt < <(sed -n '2,4p' abc.txt)
1
2
3
b
c
d
4
5
Steeldriver
quelle
2
Der rBefehl befindet sich in POSIX. Das einzige, was nicht POSIX ist (aber auch auf Nicht-GNU-Systemen üblich ist), ist der sed-Teil /dev/stdin. Wenn Sie anstelle einer Befehlsersetzung eine Pipe verwenden, ist Ihr Shell-Snippet POSIX, abgesehen von den vorhandenen Anforderungen /dev/stdin.
Gilles 'SO - hör auf böse zu sein'
Der Antwortbefehl liefert das gewünschte Ergebnis auch auf FreeBSD (einem Nicht-GNU-System).
Apostel
3

Mit awkund sed:

$ awk 'FNR==3{print;system("sed -n '2,4p' abc.txt");next};1' 123.txt 
1
2
3
b
c
d
4
5
cuonglm
quelle
2

Mit sedkönnen Sie mit dem rBefehl eine ganze Datei einfügen. Um einen Teil einer Datei einzufügen, extrahieren Sie diesen Teil und leiten Sie ihn als Eingabe an weiter sed. Sie können sedauch die Extraktion verwenden.

sed -n '2,4p' abc.txt | sed -i '3r /dev/stdin' 123.txt

Mit awkkönnen Sie eine Datei lesen und in der Mitte zu einer anderen Datei wechseln.

awk <123.txt >123.txt.new '
    1                                    # print the input line
    NR==3 {                              # after line 3 of 123.txt, …
        while (getline < "abc.txt") {
            ++inner_NR;
            if (inner_NR >= 2) print;    # start printing at line 2 from abc.txt
            if (inner_NR == 4) break;    # stop after line 4
        }
    }
' &&
mv 123.txt.new 123.txt

Sie können auch die Teile der Dateien verwenden headund tailextrahieren, die Sie kombinieren möchten.

head -n 3 <123.txt >123.txt.new &&
<abc.txt tail -n +2 | head -n 3 >>123.txt.new &&
tail -n +4 123.txt >>123.txt.new &&
mv 123.txt.new 123.txt

Sie können den head+ tailAnsatz mit kombinieren sed. Bei großen Dateien ist dies wahrscheinlich die schnellste.

<abc.txt tail -n +2 | head -n 3 | sed -i '3r /dev/stdin' 123.txt

Beachten Sie, dass alle diese Ansätze in eine temporäre Datei schreiben, in die verschoben wird 123.txt(auch die verwendeten Ansätze sed -i, da sed -idies unter der Haube geschieht).

Gilles 'SO - hör auf böse zu sein'
quelle
2

In-Place-Bearbeitung mit edund sed(an der Bash-Eingabeaufforderung):

ed -s 123.txt<<<$'3r !sed -n "2,4p" abc.txt\nw'
iruvar
quelle