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.