Comando rapido unix per visualizzare linee specifiche nel mezzo di un file?


206

Sto cercando di eseguire il debug di un problema con un server e il mio unico file di registro è un file di registro da 20 GB (senza timestamp anche! Perché le persone usano System.out.println()come registrazione? In produzione ?!)

Usando grep, ho trovato un'area del file che mi piacerebbe dare un'occhiata, linea 347340107.

Oltre a fare qualcosa di simile

head -<$LINENUM + 10> filename | tail -20 

... che richiederebbe la headlettura dei primi 347 milioni di righe del file di registro, esiste un comando semplice e veloce che scarica le righe 347340100 - 347340200 (ad esempio) sulla console?

aggiornamento Ho completamente dimenticato che grep può stampare il contesto attorno a una partita ... questo funziona bene. Grazie!


Immagino che grep debba cercare l'intero file, ci deve essere un modo meno intenso di CPU per farlo.
ojblass

Risposte:


69

con GNU-grep potresti semplicemente dire

grep --context = 10 ...

7
O più precisamente 10 righe prima: grep -B 10 ... O 10 righe dopo: grep -A 10 ...
Boy Baukema,

17
Questo comando non funziona, sotto sed -n '<start>, <end> p' funziona
Basav

5
Questo è in realtà non è ciò che si vuole, perché elaborerà l'intero file, anche se la partita è nel bit alto. A questo punto una combinazione testa / coda o coda / testa è molto più efficace.
Sklivvz,

3
Questo non soddisfa affatto la domanda posta in quanto non offre un modo per produrre una riga specifica , come richiesto.
Chris Rasys,

1
Questo non è in realtà ciò che è stato chiesto. @matt b, perché non accetti questa risposta?
user1271772

390

Ho trovato altre due soluzioni se conosci il numero di riga ma nient'altro (nessuna grep possibile):

Supponendo che tu abbia bisogno delle linee da 20 a 40,

sed -n '20,40p;41q' file_name

o

awk 'FNR>=20 && FNR<=40' file_name

6
+1: anche se potresti voler uscire dopo la stampa. Può offrire alcuni vantaggi in termini di prestazioni se il file è davvero enorme.
jaypal singh,

awk 'NR> = 20 && NR <= 40' nome_file
Sudipta Basak

2
sed -n '20, 40p; 41q 'nome_file per uscire quindi.
Snigdha Batra,

1
in particolare, si tratta dei numeri di riga iniziale e finale. Se ti trovi in ​​un file più grande, sarà "12345678,12345699p"
Abominatore del codice

1
In aggiunta al commento di @ CodeAbominator, 41qordina a sed di uscire dalla riga 41.
Brice,

116
# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files 

metodo 3 efficiente su file di grandi dimensioni

modo più veloce per visualizzare linee specifiche


Sto cercando di capire come adattare il metodo 3 per utilizzare un intervallo anziché una singola riga, ma temo che il mio sed-foo non sia all'altezza del compito.
Xiong Chiamiov

9
@XiongChiamiov Che ne dici di sed -n '1.500p; 501q' per la stampa 1-500?
Sam,

3
Il motivo per cui le prime due linee / metodi sono meno efficienti, è che continuano a elaborare tutte le linee dopo la linea 52, fino alla fine, mentre il n. 3 si interrompe dopo aver stampato la linea 52.
flow2k,

1
Questa risposta trarrebbe beneficio dallo spiegare cosa fanno tutti gli argomenti.
Bram Vanroy,

25

No, i file non sono indirizzabili in linea.

Non esiste un modo a tempo costante per trovare l'inizio della riga n in un file di testo. È necessario eseguire lo streaming del file e contare le nuove righe.

Usa lo strumento più semplice / veloce per svolgere il lavoro. Per me, l'uso headha molto più senso di grep, poiché quest'ultimo è molto più complicato. Non sto dicendo " grepè lento", in realtà non lo è, ma sarei sorpreso se fosse più veloce rispetto heada questo caso. In headpratica sarebbe un errore .


2
A meno che le righe non abbiano una larghezza fissa in byte, non si sa dove spostare il puntatore del file senza contare i nuovi caratteri di riga dall'inizio del file.
Joseph Lust,

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post.
exhuma,

@exhuma Hai ragione. Ho riscritto. Sette anni fa mi sono seccato. :)
Rilassati

20

Che dire:

tail -n +347340107 filename | head -n 100

Non l'ho provato, ma penso che funzionerebbe.


No, di solito tail ha un limite di 256 kilobyte o simili, a seconda della versione e del sistema operativo.
Antti Rytsölä,

💪 yessire miller
dctremblay,

13

Preferisco solo entrare lesse

  • digitando per 50%andare a metà del file,
  • 43210G per andare alla linea 43210
  • :43210 fare lo stesso

e cose così.

Ancora meglio: premi vper iniziare a modificare (in vim, ovviamente!), In quella posizione. Ora, nota che vimha gli stessi collegamenti chiave!


12

Dapprima avrei diviso il file in alcuni più piccoli come questo

$ split --lines=50000 /path/to/large/file /path/to/output/file/prefix

e quindi grep sui file risultanti.


concordato, interrompere il log in e creare un cron job per farlo correttamente. usa logrotate o qualcosa di simile per evitare che diventino così enormi.
Tanj,

9

È possibile utilizzare il excomando, un editor Unix standard (parte di Vim ora), ad es

  • visualizzare una riga singola (es. seconda):

    ex +2p -scq file.txt

    sintassi sed corrispondente: sed -n '2p' file.txt

  • gamma di linee (ad es. 2-5 linee):

    ex +2,5p -scq file.txt

    sintassi sed: sed -n '2,5p' file.txt

  • dalla riga indicata fino alla fine (ad es. dalla 5a alla fine del file):

    ex +5,p -scq file.txt

    sintassi sed: sed -n '2,$p' file.txt

  • intervalli di linee multiple (ad esempio 2-4 e 6-8 linee):

    ex +2,4p +6,8p -scq file.txt

    sintassi sed: sed -n '2,4p;6,8p' file.txt

I comandi sopra possono essere testati con il seguente file di test:

seq 1 20 > file.txt

Spiegazione:

  • +o -cseguito dal comando: esegui il comando (vi / vim) dopo aver letto il file,
  • -s - modalità silenziosa, utilizza anche il terminale corrente come output predefinito,
  • qseguito da -cè il comando per uscire dall'editor (aggiungi !per chiudere forzatamente, ad es -scq!.).

7

Se il tuo numero di riga è 100 da leggere

head -100 filename | tail -1

6

Ottenere ack

Installazione di Ubuntu / Debian:

$ sudo apt-get install ack-grep

Quindi eseguire:

$ ack --lines=$START-$END filename

Esempio:

$ ack --lines=10-20 filename

Da $ man ack:

--lines=NUM
    Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
    The lines are always output in ascending order, no matter the order given on the command line.

1
Questo, a me sembra il comando con la sintassi più intuitiva tra tutte le risposte qui.
nzn,

Dalla versione 2.999_06 del 10 gennaio 2019 il --linesparametro è stato rimosso.
Burny

4

sed dovrà leggere anche i dati per contare le righe. L'unico modo in cui un collegamento sarebbe possibile sarebbe che ci sia contesto / ordine nel file su cui operare. Ad esempio, se c'erano righe di registro precedute da una data / ora a larghezza fissa, ecc., È possibile utilizzare l' utilità look unix per cercare binariamente i file per date / orari particolari


4

Uso

x=`cat -n <file> | grep <match> | awk '{print $1}'`

Qui otterrai il numero di riga in cui si è verificata la corrispondenza.

Ora puoi usare il seguente comando per stampare 100 linee

awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>

oppure puoi usare anche "sed"

sed -n "${x},${x+100}p" <file>

Se hai più di una partita, usa: "awk 'NR == 1 {print $ 1}" per la prima partita e così via
Ramana Reddy,

2

Con sed -e '1,N d; M q'potrai stampare le linee da N + 1 a M. Questo è probabilmente un po 'meglio di allora grep -Cin quanto non cerca di abbinare le linee a un motivo.


-eè facoltativo qui.
flow2k,

2

Sulla base della risposta di Sklivvz, ecco una bella funzione che si può inserire in un .bash_aliasesfile. È efficiente su file di grandi dimensioni quando si stampa roba dalla parte anteriore del file.

function middle()
{
    startidx=$1
    len=$2
    endidx=$(($startidx+$len))
    filename=$3

    awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
}

1

Per visualizzare una linea dalla a <textfile>alla sua <line#>, basta fare questo:

perl -wne 'print if $. == <line#>' <textfile>

Se vuoi un modo più potente per mostrare una gamma di linee con espressioni regolari - non dirò perché grep sia una cattiva idea per farlo, dovrebbe essere abbastanza ovvio - questa semplice espressione ti mostrerà la tua gamma in un passaggio singolo che è quello che vuoi quando hai a che fare con file di testo di ~ 20 GB:

perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>

(suggerimento: se il tuo regex ha /dentro, usa qualcosa di simile m!<regex>!invece)

Questo verrebbe stampato a <filename>partire dalla riga corrispondente <regex1>fino alla (e inclusa) riga corrispondente <regex2>.

Non ci vuole un mago per vedere come alcune modifiche possono renderlo ancora più potente.

Ultima cosa: il perl, poiché è un linguaggio maturo, ha molti miglioramenti nascosti per favorire la velocità e le prestazioni. Con questo in mente, lo rende la scelta ovvia per tale operazione poiché è stato originariamente sviluppato per la gestione di file di registro di grandi dimensioni, testo, database, ecc.


davvero, non mi sembra così, dato che quando si esegue un comando perl più complicato di quanto si pensi, eseguendo 2+ programmi collegati (più in basso nella pagina), e, penso che tu stia effettivamente dicendo perché ho digitato più di una spiegazione che ti ha richiesto di LEGGERE, dal momento che ci sono ugualmente complessi (o più) in fondo alla pagina che non sono stati fatti saltare fuori dall'acqua ... sheesh
osirisgothra

Si noti che l'utente ha richiesto una serie di righe: il tuo esempio può essere banalmente adattato.
Sklivvz,

0

Puoi provare questo comando:

egrep -n "*" <filename> | egrep "<line number>"

0

Facile con perl! Se vuoi ottenere la linea 1, 3 e 5 da un file, dì / etc / passwd:

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

1
Dici che è facile con awk, ma invece l'hai fatto in perl?
Prigioniero 13

0

Sono sorpreso solo un'altra risposta (di Ramana Reddy) suggerita di aggiungere numeri di riga all'output. Di seguito cerca il numero di riga richiesto e colora l'output.

file=FILE
lineno=LINENO
wb="107"; bf="30;1"; rb="101"; yb="103"
cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }

Le risposte con codice tendono solo a essere contrassegnate per l'eliminazione. Potresti aggiungere qualche commento su come questo risolve il problema?
Graham,
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.