Przenieś pierwsze N ​​wierszy wyniku na koniec bez użycia pliku tymczasowego

11

Wyobraź sobie wynik polecenia takiego jak

44444
55555
11111
22222
33333

jak mogę wyciągnąć pierwsze N ​​wierszy (pierwsze dwa w powyższym przykładzie) i dołączyć je na końcu, ale bez użycia pliku tymczasowego (a więc tylko przy użyciu potoków)?

11111
22222
33333
44444
55555

Coś w stylu | sed -n '3,5p;1,2p'(co oczywiście nie działa, ponieważ sed nie dba o kolejność poleceń).

Peter Uhnak
źródło
2
Dlaczego nie możemy użyć pliku tymczasowego?
Braiam 20.04.16

Odpowiedzi:

13

Po prostu skopiuj te linie do bufora wstrzymania (a następnie je usuń), a gdy w ostatnim wierszu dołącz zawartość bufora wstrzymania do przestrzeni wzorów:

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

Ze gnu sedmożna napisać go jako

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

Oto inny sposób z ol ' ed( rwprowadza wyjście some commanddo bufora tekstowego, a następnie musuwa wiersze 1,NUMBERpo $pierwszym):

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

Zauważ, że - jak wskazano - oba się nie powiedzie, jeśli wyjście ma mniej niż NUMBER+1 linii. Bardziej solidnym podejściem byłoby ( gnu sedskładnia):

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

ten usuwa tylko wiersze z tego zakresu, o ile nie są one ostatnim wierszem ( $!d) - w przeciwnym razie nadpisuje przestrzeń wzorca zawartością bufora wstrzymania ( g), a następnie qusuwa (po wydrukowaniu bieżącej przestrzeni wzorca).

don_crissti
źródło
2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'działa również przenośnie (zwróć uwagę, że niektóre sedimplementacje nie mogą pomieścić więcej niż kilka kilobajtów w przestrzeni
ładunkowej
@ StéphaneChazelas - dziękuję za dane wejściowe - zwykle używam jednego polecenia w wierszu, ponieważ wiem na pewno, że jest to przenośne - składnia wielu wyrażeń zawsze była dla mnie trochę myląca, np. Dokumenty z posixem mówią: poprzedzony <nową>> więc według nich nie powinno tak być sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
don_crissti 20.04.16
4
Zazwyczaj nowy -ezastępuje nowy wiersz. d;}nie jest jeszcze POSIX, ale przenośny. Zostanie to naprawione w następnej specyfikacji POSIX. Zobacz austingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas
2
@don_crissti dziękuję! Byłoby fajnie, gdybyś mógł również podać krótkie wyjaśnienie, dlaczego to działa. (Oczywiście, że przyjrzę się temu, ale to byłaby cenniejsza odpowiedź.)
Peter Uhnak,
W mojej głowie, rzecz niemożliwa do przeniesienia, 1,NUMBER{H;1h;d;}to brak średnika bezpośrednio po nawiasie otwierającym . To mógł być tylko błąd w SunOS 4.1, sedktórego obejście jest nadal w moich palcach 20 lat później.
zwolnić
11

awkPodejście:

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

Jedną z korzyści w stosunku do sedpodejścia @ don_crissti jest to, że nadal działa (wysyła wiersze), jeśli wyjście ma nwiersze lub mniej.

Stéphane Chazelas
źródło
Możesz zastąpić na stałe \nORS, aby działało to z innymi separatorami rekordów (powiedz, że chcesz używać akapitów itp.).
fedorqui
6

Mam xclipi dzięki temu można to zrobić w następujący sposób:

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

Oto jego opis:

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)

źródło
3
+1 za kreatywne (błędne) użycie xclip. Odpowiedź wymaga dostępnego / działającego serwera X.
jofel 20.04.16
3

Sposób Perla:

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

Lub to samo napisane mniej tajemniczo:

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

Na przykład:

$ cat file
44444
55555
11111
22222
33333

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

Wyjaśnienie

  • -ne: przeczytaj plik wejściowy / strumień wiersz po wierszu i zastosuj skrypt podany przez -edla każdego wiersza.
  • $.<3: $.to bieżący numer linii, więc zmień 3na liczbę linii, które chcesz przesunąć.
  • $.<3?($f.=$_):print;: to jest operator warunkowy, ogólny format brzmi: condition ? case1 : case2będzie działał, case1jeśli conditionjest prawdziwy, a case2jeśli jest fałszywy. Tutaj, jeśli bieżący numer linii jest mniejszy niż 3, dołącza bieżącą linię ( $_) do zmiennej $fi, jeśli numer linii jest większy niż 3, drukuje.
  • }{ print $f: }{jest to skrót perla END{}. Będzie działał po przetworzeniu wszystkich linii wejściowych. W tym momencie zbierzemy wszystkie linie, które chcemy przesunąć i wydrukujemy wszystkie, które chcemy zostawić w spokoju, więc wydrukuj linie zapisane jako $f.
terdon
źródło
1
jeśli chodzi o twoją wersję golfa, kilka postaci można usunąćperl -ne '$.<3?$f.=$_:print}{print $f
123
1

Użyj POSIX ex. Tak, jest przeznaczony do edycji plików, ale będzie działał w potoku.

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

Może to mieć dowolne dowolne polecenia dodane na początku lub na końcu potoku i będzie działać tak samo. Jeszcze lepiej, biorąc pod uwagę obecność /dev/stdin, jest zgodny z POSIX.

(Nie wiem, czy /dev/stdinjest określony w POSIX, czy nie, ale widzę, że jest obecny zarówno w systemie Linux, jak i Mac OS X.)

Ma to przewagę w zakresie czytelności w porównaniu do korzystania sedz miejsca trzymania - po prostu mówisz ex„przenieś te linie do końca” i tak się dzieje. (Reszta poleceń oznacza „wydrukuj bufor” i „exit”, które również są dość czytelne.)

NB: exPolecenie podane powyżej zakończy się niepowodzeniem, jeśli jako dane wejściowe podano mniej niż 2 linie.

Dalsza lektura:

Dzika karta
źródło
0

Krótki pythonfragment:

#!/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='')

Przekaż nazwę pliku jako pierwszy argument i liczbę wierszy do przeniesienia jako drugi argument.

Przykład:

$ 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
źródło
0

Jeśli możesz zapisać całe wyjście w pamięci:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile
Glenn Jackman
źródło
0

Oto kolejna opcja, która wymaga GNU sed:

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

-usprawia, że ​​GNU nie jest sedbuforowane, tak że sedpolecenie nie zużywa więcej niż 3 wiersze STDIN. Podstawienie polecenia usuwa puste linie, dzięki czemu polecenie nie zawiera pustych linii na końcu danych wyjściowych, jeśli trzeci, trzeci i drugi lub trzeci, drugi i pierwszy wiersz są puste.

Możesz także zrobić coś takiego:

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

Bez sponge /dev/stdout|polecenia zawiodłoby przy długich wejściach. sponge /dev/stdoutmoże być również zastąpione przez tac|tac, nawet jeśli drukuje to na przykład, a\ncbjeśli wejście jest a\nb\nctam, gdzie \njest linia, lub (x=$(cat);printf %s\\n "$x"), nawet jeśli usuwa puste linie z końca wejścia. Powyższe polecenie usuwa pierwszy wiersz, gdy liczba wierszy wejścia wynosi jeden lub dwa.

nisetama
źródło