Sposta le prime N righe di output alla fine senza utilizzare il file temporaneo


11

Immagina un output di un comando simile

44444
55555
11111
22222
33333

come posso strappare le prime N righe (le prime due nell'esempio sopra) e aggiungerle alla fine, ma senza usare il file temporaneo (quindi usando solo le pipe)?

11111
22222
33333
44444
55555

Qualcosa sulla falsariga di | sed -n '3,5p;1,2p'(che ovviamente non funziona come sed non si preoccupa dell'ordine dei comandi).


2
Perché non possiamo usare un file temporaneo?
— Braiam,

Risposte:


13

Basta copiare quelle righe nel buffer di conservazione (quindi eliminarle) e quando nell'ultima riga aggiungere il contenuto del buffer di conservazione allo spazio modello:

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

Con gnu sedte potresti scriverlo come

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

Ecco un altro modo con ol ' ed( rperline l'output some commandnel buffer di testo e poi moves righe 1,NUMBERdopo $quello precedente):

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

Si noti che, come sottolineato, entrambi falliranno se l'output ha meno di NUMBER+1 righe. Un approccio più solido sarebbe ( gnu sedsintassi):

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

questo elimina solo le linee in quell'intervallo fintanto che non sono l'ultima riga ( $!d) - altrimenti sovrascrive lo spazio del modello con il contenuto del buffer di mantenimento ( g) e quindi lo utilizza q(dopo aver stampato lo spazio del modello corrente).


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'funziona anche in modo portabile (si noti che alcune sedimplementazioni non possono contenere più di pochi kilobyte nello spazio di attesa, quindi NUMBER non può essere troppo grande lì)
— Stéphane Chazelas

@ StéphaneChazelas - grazie per l'input - Di solito vado con un comando per riga poiché so per certo che è portatile - la sintassi delle espressioni multiple è sempre stata un po 'confusa per me, ad esempio i documenti posix dicono "Il <brace> deve essere preceduto da un <newline> " quindi secondo loro non dovrebbe essere sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
— don_crissti,

4
In genere, una nuova -esostituisce una nuova riga. d;}non è ancora POSIX ma portatile. Ciò verrà risolto nelle prossime specifiche POSIX. Vedi austingroupbugs.net/view.php?id=944#c2720
— Stéphane Chazelas

2
@don_crissti grazie! sarebbe bello se si potesse anche includere una breve spiegazione del perché funziona. (Certo che vado a cercarlo, ma darebbe una risposta più preziosa.)
— Peter Uhnak,

Nella mia testa, l'importante 1,NUMBER{H;1h;d;}è non avere un punto e virgola immediatamente dopo il controvento di apertura . Potrebbe essere stato solo un bug in SunOS 4.1 la sedcui soluzione alternativa è ancora collegata alle mie dita 20 anni dopo, però.
— zwol,

11

Un awkapproccio:

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

Uno dei vantaggi nel corso di @ don_crissti sedapproccio è che esso funziona ancora (uscite le linee) se l'uscita ha nlinee o meno.


Potresti voler sostituire l'hardcoded \ncon ORS, in modo che funzionasse con altri separatori di record (diciamo che vuoi usare paragrafi, ecc.).
— fedorqui,

6

Ho xclipe con esso questo può essere fatto in questo modo:

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

Ecco la sua descrizione:

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)

3
+1 per l'uso creativo (errato) di xclip. La risposta richiede un server X accessibile / in esecuzione.
— jofel,

3

Un modo Perl:

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

Oppure, la stessa cosa scritta in modo meno criptico:

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

Per esempio:

$ cat file
44444
55555
11111
22222
33333

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

Spiegazione

  • -ne: legge il file di input / stream riga per riga e applica lo script fornito da -eciascuna riga.
  • $.<3: $.è il numero di riga corrente, quindi modifica 3il numero di righe che desideri spostare.
  • $.<3?($f.=$_):print;: questo è l'operatore condizionale, il formato generale è condition ? case1 : case2, verrà eseguito case1se conditionè vero e case2se è falso. Qui, se il numero di riga corrente è inferiore a 3, aggiunge la riga corrente ( $_) alla variabile $fe, se il numero di riga è maggiore di 3, viene stampato.
  • }{ print $f: }{è la scorciatoia perl per END{}. Funzionerà dopo che tutte le linee di input sono state elaborate. A questo punto, avremo raccolto tutte le linee che vogliamo spostare e avremo stampato tutte quelle che vogliamo lasciare da sole, quindi stampa le linee salvate come $f.

1
per quanto riguarda la tua versione golfizzata, alcuni personaggi possono essere rimossiperl -ne '$.<3?$f.=$_:print}{print $f
— 123

1

Usa POSIX ex. Sì, è destinato alla modifica dei file, ma funzionerà in una pipeline.

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

Questo può avere qualsiasi comando arbitrario aggiunto all'inizio o alla fine della pipeline e funzionerà allo stesso modo. Meglio ancora, data la presenza di /dev/stdin, è conforme POSIX.

(Non so se /dev/stdinè specificato in POSIX o meno, ma vedo che è presente sia in Linux che in Mac OS X.)

Questo ha un vantaggio in termini di leggibilità rispetto all'uso seddello spazio di attesa: basta dire ex"sposta queste righe alla fine" e lo fa. (Il resto dei comandi significa "stampa il buffer" e "esci", che sono anche abbastanza leggibili.)

NB: Il excomando indicato sopra fallirà se vengono dati meno di 2 righe come input.

Ulteriori letture:


0

Un breve pythonframmento:

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

Passa il nome file come primo argomento e il numero di righe per spostarlo come secondo argomento.

Esempio:

$ 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

0

Se è possibile memorizzare l'intero output in memoria:

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

0

Ecco un'altra opzione che richiede GNU sed:

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

-urende GNU senza sedbuffer in modo che il sedcomando non consumi più di 3 righe di STDIN. La sostituzione del comando rimuove le righe vuote, in modo che il comando non includa le righe vuote alla fine dell'output se la terza, terza e seconda, o terza, seconda e prima riga sono vuote.

Puoi anche fare qualcosa del genere:

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

Senza sponge /dev/stdout|il comando fallirebbe con input lunghi. sponge /dev/stdoutpuò anche essere sostituito con tac|tac, anche se ciò stampa ad esempio a\ncbse l'input è a\nb\ncdove \nè un avanzamento riga, o con (x=$(cat);printf %s\\n "$x"), anche se ciò rimuove le righe vuote dalla fine dell'input. Il comando sopra elimina la prima riga quando il numero di righe dell'input è uno o due.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.