Come visualizzare determinate righe da un file di testo in Linux?


86

Immagino che tutti conoscano le utili utilità di Linux cmd line heade tail. headti permette di stampare le prime X linee di un file, tailfa lo stesso ma stampa la fine del file. Qual è un buon comando per stampare la metà di un file? qualcosa del genere middle --start 10000000 --count 20(stampare la riga 10'000'000 fino alla 10'000'010).

Sto cercando qualcosa che gestirà in modo efficiente file di grandi dimensioni. Ho provato tail -n 10000000 | head 10ed è terribilmente lento.


Risposte:


112
sed -n '10000000,10000020p' filename

Potresti essere in grado di accelerare un po 'in questo modo:

sed -n '10000000,10000020p; 10000021q' filename

In questi comandi, l'opzione -nprovoca la sed"soppressione della stampa automatica dello spazio del motivo". Il pcomando "stampa lo spazio modello corrente" e il qcomando "Esci immediatamente [s] lo script sed senza elaborare altri input ..." Le virgolette sono dalla sed manpagina .

A proposito, il tuo comando

tail -n 10000000 filename | head 10

inizia alla decimilionesima riga dalla fine del file, mentre il comando "centrale" sembrerebbe iniziare alla decimilionesima dall'inizio, il che equivarrebbe a:

head -n 10000010 filename | tail 10

Il problema è che per i file non ordinati con linee di lunghezza variabile qualsiasi processo dovrà passare attraverso il conteggio delle nuove righe. Non c'è modo di scorciatoia.

Se, tuttavia, il file viene ordinato (un file di registro con timestamp, ad esempio) o ha linee di lunghezza fissa, è possibile cercare nel file in base a una posizione in byte. Nell'esempio del file di registro, è possibile eseguire una ricerca binaria per un intervallo di volte, come fa il mio script Python qui *. Nel caso del file con lunghezza fissa del record, è davvero semplice. Basta cercare linelength * linecountcaratteri nel file.

* Continuo a postare un altro aggiornamento a quello script. Forse ci proverò uno di questi giorni.


Ecco una sedversione di Charles' middlela funzione: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Gestirà più argomenti di file, nomi di file con spazi, ecc. Più file vengono elaborati insieme come se fossero stati catturati nello stesso modo in cui sednormalmente (quindi la metà del file 100 1001 file1 file2 si estenderebbe all'inizio del primo file all'inizio del secondo se il primo ha meno di 1100 linee).
Dennis Williamson,

La funzione nel mio commento precedente può essere chiamata con un parametro nome file: middle startline count filenameo più nomi di file: middle startline count file1 file2 file3o con reindirizzamento: middle startline count < filenameo in una pipe: some_command | conteggio della linea di partenza centrale` oppurecat file* | middle startline count
Dennis Williamson,

Il "nel tuo comando sed non dovrebbe essere un"? Non riesco a farlo funzionare con il backtick ma funziona bene con la singola citazione.
Ian Hunter,

@beanland: Sì, è un errore di battitura. L'ho risolto. Grazie.
Dennis Williamson,

1
@kev: ho aggiunto alcune spiegazioni alla mia risposta.
Dennis Williamson,

28

Ho scoperto il seguente uso di sed

sed -n '10000000,+20p'  filename

Spero sia utile a qualcuno!


Buono a sapersi che esiste un'alternativa all'ultima riga dell'argomento proposta da Dennis: una riga conta come secondo sed -nargomento che la rende abbastanza leggibile.
user3123159

Un esempio di utilizzo: extract_lines(){sed -n "$1,+$2p" <file>}che scrive su stdout.
user3123159

4

Questa è la mia prima pubblicazione qui! Comunque, questo è facile. Diciamo che vuoi estrarre la linea 8872 dal tuo file chiamato file.txt. Ecco come lo fai:

cat -n file.txt | grep '^ * 8872'

Ora la domanda è di trovare 20 righe dopo questo. Per farlo, lo fai

cat -n file.txt | grep -A 20 '^ * 8872'

Per le linee intorno o prima vedi i flag -B e -C nel manuale grep.


Sebbene sia tecnicamente corretto e sia un modo interessante per farlo su un file di dimensioni ragionevoli, sono curioso della sua efficacia quando si lavora con file delle dimensioni richieste dal poster.
Jenny D,

Righe multiple: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight

cat -n file.txt | grep '^ *1'cede tutte le linee che hanno 1 alla loro destra. Come emettere la riga 1 con questa tecnica? So di poter dirigere -n 1 .... ma come usare grep?
Sean87,

1

La risposta sed di Dennis è la strada da percorrere. Ma usando solo testa e coda, sotto bash:

middle () {head -n $ [$ 1 + $ 2] | tail -n $ 2; }

Questo analizza due volte le prime $ 1 + $ 2, quindi è molto peggio della risposta di Dennis. Ma non è necessario ricordare tutte quelle lettere sed per usarlo ....


L'uso $[...]è deprecato, almeno in Bash. Inoltre, ti manca un parametro di file.
Dennis Williamson,

@Dennis: nessun parametro mancante: dovresti usarlo su stdin, come da middle 10 10 < /var/log/auth.log.
Charles Stewart,

1

Utilizzare il comando seguente per ottenere il particolare intervallo di linee

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Qui debug.log è il mio file che consiste in una mancanza di righe e ho usato per stampare le righe da 1220974 numero di riga a 1513793 in un file test.log. spero che sia utile per catturare la gamma di linee.


La stessa risposta di serverfault.com/a/641252/140016 . Downvoted.
Cacciatore di cervi,

Non è la stessa risposta. Questo dovrebbe essere più veloce per file di grandi dimensioni in quanto si interrompe effettivamente dopo aver stampato l'ultima riga invece di continuare la scansione del file.
fobico il

0

Una versione oneliner color rubino.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Può essere utile a qualcuno. Le soluzioni con 'sed' fornite da Dennis e Dox sono molto belle, anche perché sembrano più veloci.


0

Puoi usare 'nl'.

nl filename | grep <line_num>

0

Ad esempio, questo awk stamperà linee tra 20 e 40

awk '{if ((NR> 20) && (NR <40)) stampa $ 0}' / etc / passwd


0

Se conosci i numeri di riga, supponi di voler ottenere le righe 1, 3 e 5 da un file, dì / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

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.