Verschieben Sie die ersten N Ausgabezeilen bis zum Ende, ohne eine temporäre Datei zu verwenden

11

Stellen Sie sich eine Ausgabe eines Befehls wie vor

44444
55555
11111
22222
33333

Wie kann ich die ersten N Zeilen (die ersten beiden im obigen Beispiel) ziehen und am Ende anhängen, ohne jedoch eine temporäre Datei zu verwenden (also nur Pipes)?

11111
22222
33333
44444
55555

Etwas in der Art von | sed -n '3,5p;1,2p'(was offensichtlich nicht funktioniert, da sed sich nicht um die Reihenfolge der Befehle kümmert).

Peter Uhnak
quelle
2
Warum können wir keine temporäre Datei verwenden?
Braiam

Antworten:

13

Kopieren Sie diese Zeilen einfach in den Haltepuffer (und löschen Sie sie dann) und hängen Sie in der letzten Zeile den Inhalt des Haltepuffers an den Musterbereich an:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

Mit gnu sedkönnte man es schreiben als

some command | sed '1,NUMBER{H;1h;d;};$G'

Hier ist ein anderer Weg mit ol ' ed(es rführt die Ausgabe von some commandin den Textpuffer und übergibt dann mZeilen 1,NUMBERnach dem letzten $):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

Beachten Sie, dass - wie bereits erwähnt - beide fehlschlagen, wenn die Ausgabe weniger als NUMBER+1 Zeilen enthält. Ein soliderer Ansatz wäre ( gnu sedSyntax):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

Dieser löscht nur Zeilen in diesem Bereich, solange sie nicht die letzte Zeile sind ( $!d). Andernfalls wird der Musterbereich mit dem Haltepufferinhalt ( g) überschrieben und dann qbeendet (nach dem Drucken des aktuellen Musterbereichs).

don_crissti
quelle
2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'funktioniert auch portabel (beachten Sie, dass einige sedImplementierungen nicht mehr als ein paar Kilobyte im Haltebereich enthalten können , sodass NUMBER dort nicht zu groß sein kann)
Stéphane Chazelas
@ StéphaneChazelas - Dank für die Eingabe - ich in der Regel mit einem Befehl pro Zeile gehen , wie ich sicher wissen , dass portable ist - die mehrere Ausdrücke Syntax ein wenig hat verwirrend ich zB die Posix - docs sagen immer „Die <rechte Klammer> wird sein vorangestellt von einem <newline> " also sollte das laut ihnen nicht sein sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
don_crissti
4
In der Regel -eersetzt eine neue Zeile eine neue Zeile. d;}ist noch nicht POSIX, aber portabel. Dies wird in der nächsten POSIX-Spezifikation behoben. Siehe austingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas
2
@don_crissti danke! Es wäre cool, wenn Sie auch eine kurze Erklärung hinzufügen könnten, warum es funktioniert. (Natürlich werde ich es nachschlagen, aber es wäre eine wertvollere Antwort.)
Peter Uhnak
In meinem Kopf 1,NUMBER{H;1h;d;}ist es nicht tragbar, nicht unmittelbar nach der Eröffnungsklammer ein Semikolon zu haben . Das könnte ein Fehler in SunOS 4.1 gewesen sein, seddessen Problemumgehung 20 Jahre später immer noch in meinen Fingern steckt.
zwol
11

Ein awkAnsatz:

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

Ein Vorteil gegenüber dem sedAnsatz von @ don_crissti besteht darin, dass es immer noch funktioniert (die Zeilen ausgibt), wenn die Ausgabe nZeilen oder weniger enthält.

Stéphane Chazelas
quelle
Möglicherweise möchten Sie den fest codierten \ndurch ORS ersetzen , damit dies mit anderen Datensatztrennzeichen funktioniert (z. B. wenn Sie Absätze usw. verwenden möchten).
Fedorqui
6

Ich habe xclipund damit kann dies auf folgende Weise geschehen:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

Hier ist seine Beschreibung:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)
Sergey Kurenkov
quelle
3
+1 für die kreative (falsche) Verwendung von xclip. Die Antwort benötigt einen zugänglichen / laufenden X-Server.
Jofel
3

Ein Perl-Weg:

perl -ne '$.<3?($f.=$_):print;}{print $f'

Oder dasselbe weniger kryptisch geschrieben:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

Beispielsweise:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

Erläuterung

  • -ne: Lesen Sie die Eingabedatei / den Stream zeilenweise und wenden Sie das von -ejeder Zeile angegebene Skript an.
  • $.<3: $.ist die aktuelle Zeilennummer. Wechseln Sie also 3zu der Anzahl der Zeilen, die Sie verschieben möchten.
  • $.<3?($f.=$_):print;: Dies ist der bedingte Operator, das allgemeine Format ist condition ? case1 : case2, es wird ausgeführt, case1wenn conditiones wahr ist und case2wenn es falsch ist. Wenn hier die aktuelle Zeilennummer kleiner als 3 ist, wird die aktuelle Zeile ( $_) an die Variable angehängt , $fund wenn die Zeilennummer größer als 3 ist, wird gedruckt.
  • }{ print $f: das }{ist Perl Abkürzung für END{}. Es wird ausgeführt, nachdem alle Eingabezeilen verarbeitet wurden. Zu diesem Zeitpunkt haben wir alle Zeilen gesammelt, die wir verschieben möchten, und alle Zeilen gedruckt, die wir in Ruhe lassen möchten. Drucken Sie also die Zeilen aus, unter denen gespeichert wurde $f.
terdon
quelle
1
In Bezug auf Ihre Golfversion können einige Charaktere entfernt werdenperl -ne '$.<3?$f.=$_:print}{print $f
123
1

Verwenden Sie POSIX ex. Ja, es ist für die Dateibearbeitung vorgesehen, funktioniert jedoch in einer Pipeline.

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

Dies kann beliebige Befehle am Anfang oder Ende der Pipeline hinzufügen und funktioniert genauso. Besser noch, angesichts des Vorhandenseins von /dev/stdinist es POSIX-konform.

(Ich weiß nicht, ob /dev/stdinin POSIX angegeben ist oder nicht, aber ich sehe, dass es sowohl in Linux als auch in Mac OS X vorhanden ist.)

Dies hat einen Vorteil in Bezug auf die Lesbarkeit gegenüber der Verwendung seddes Haltebereichs - Sie sagen einfach ex"Verschieben dieser Zeilen bis zum Ende" und dies geschieht. (Die restlichen Befehle bedeuten "Drucken des Puffers" und "Beenden", die ebenfalls gut lesbar sind.)

NB: Der exoben angegebene Befehl schlägt fehl, wenn weniger als 2 Zeilen als Eingabe angegeben werden.

Weiterführende Literatur:

Platzhalter
quelle
0

Ein kurzer pythonAusschnitt:

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

Übergeben Sie den Dateinamen als erstes Argument und die Anzahl der Zeilen, die als zweites Argument verschoben werden sollen.

Beispiel:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222
heemayl
quelle
0

Wenn Sie die gesamte Ausgabe im Speicher speichern können:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile
Glenn Jackman
quelle
0

Hier ist eine weitere Option, für die GNU erforderlich ist sed:

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-umacht GNU sedungepuffert, so dass der sedBefehl nicht mehr als 3 Zeilen STDIN verbraucht. Die Befehlssubstitution entfernt leere Zeilen, sodass der Befehl keine leeren Zeilen am Ende der Ausgabe enthält, wenn die dritte, dritte und zweite oder dritte, zweite und erste Zeile leer sind.

Sie können auch so etwas tun:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

Ohne sponge /dev/stdout|den Befehl würde mit langen Eingaben fehlschlagen. sponge /dev/stdoutkann auch ersetzt werden , tac|tacobwohl der Druck zum Beispiel, auch a\ncbwenn der Eingang , a\nb\ncwo \nein Zeilenvorschub ist, oder mit (x=$(cat);printf %s\\n "$x"), auch wenn dies entfernt leeren Zeilen ab dem Ende des Eingangs. Der obige Befehl löscht die erste Zeile, wenn die Anzahl der Zeilen der Eingabe eins oder zwei beträgt.

Nisetama
quelle