Strumento Bash per ottenere l'ennesima riga da un file


606

Esiste un modo "canonico" per farlo? Ho usato head -n | tail -1il trucco, ma mi chiedevo se esiste uno strumento Bash che estrae specificamente una linea (o un intervallo di linee) da un file.

Per "canonico" intendo un programma la cui funzione principale lo sta facendo.


10
Il "modo Unix" è quello di concatenare strumenti che fanno bene il loro rispettivo lavoro. Quindi penso che tu abbia già trovato un metodo molto adatto. Altri metodi includono awke sedsono sicuro che qualcuno può inventare un one-liner Perl o giù di lì;)
0xC0000022L

3
Il doppio comando suggerisce che la head | tailsoluzione non è ottimale. Altre soluzioni quasi ottimali sono state suggerite.
Jonathan Leffler,

Hai eseguito dei benchmark su quale soluzione è la più veloce per un caso medio?
Marcin,

5
Benchmark (per un intervallo) alla linea di gatto X alla linea Y su un enorme file su Unix e Linux . (cc @Marcin, nel caso ti stia ancora chiedendo dopo due + anni)
Kevin

6
La head | tailsoluzione non funziona se si esegue una query su una riga che non esiste nell'input: stamperà l'ultima riga.
jarno

Risposte:


803

heade pipe con tailsarà lento per un file enorme. Vorrei suggerire in sedquesto modo:

sed 'NUMq;d' file

Dov'è NUMil numero della linea che si desidera stampare; quindi, ad esempio, sed '10q;d' fileper stampare la decima riga di file.

Spiegazione:

NUMqsi chiuderà immediatamente quando il numero di riga è NUM.

deliminerà la linea invece di stamparla; questo è inibito sull'ultima riga perché qfa sì che il resto dello script venga saltato all'uscita.

Se hai NUMuna variabile, ti consigliamo di utilizzare virgolette doppie anziché singole:

sed "${NUM}q;d" file

44
Per coloro che chiedono, questa soluzione sembra circa 6-9 volte più veloce della sed -n 'NUMp'e sed 'NUM!d'soluzioni proposte qui di seguito.
Skippy le Grand Gourou,

75
Penso che tail -n+NUM file | head -n1sia probabilmente altrettanto veloce o più veloce. Almeno, è stato (significativamente) più veloce sul mio sistema quando l'ho provato con NUM che era 250000 su un file con mezzo milione di righe. YMMV, ma non vedo davvero perché dovrebbe.
rici,

2
@rici (revisione del commento precedente) Su Linux (Ubuntu 12.04, Fedora 20), l'utilizzo catè effettivamente più veloce (quasi due volte più veloce), ma solo se il file non è stato ancora memorizzato nella cache . Una volta che il file viene memorizzato nella cache , l'uso diretto dell'argomento nome file è più rapido (circa 1/3 più veloce), mentre le catprestazioni rimangono invariate. Curiosamente, su OS X 10.9.3 nulla di tutto ciò sembra fare alcuna differenza: cat/ no cat, file memorizzato nella cache o meno. @anubhava: piacere mio.
mklement0,

2
@SkippyleGrandGourou: Data la natura specifica di questa ottimizzazione , anche i tuoi intervalli di numeri sono inutili come una dichiarazione generale . L'unico aspetto generale è questo: (a) questa ottimizzazione può essere tranquillamente applicata a tutti gli input, (b) gli effetti varieranno da nessuno a drammatico , a seconda dell'indice della linea cercata rispetto al numero di linee complessive.
mklement0

17
sed 'NUMqprodurrà i primi NUMfile ed ;deliminerà tutti tranne l'ultima riga.
anubhava,

304
sed -n '2p' < file.txt

stamperà la seconda riga

sed -n '2011p' < file.txt

2011th line

sed -n '10,33p' < file.txt

linea 10 fino alla linea 33

sed -n '1p;3p' < file.txt

1a e 3a riga

e così via...

Per aggiungere linee con sed, puoi controllare questo:

sed: inserire una linea in una determinata posizione


6
@RafaelBarbosa il <in questo caso non è necessario. Semplicemente, preferisco usare i reindirizzamenti, perché spesso ho usato reindirizzamenti come sed -n '100p' < <(some_command)- quindi, sintassi universale :). NON è meno efficace, perché il reindirizzamento viene eseguito con la shell durante il fork di se stesso, quindi ... è solo una preferenza ... (e sì, è un carattere più lungo) :)
jm666,

1
@ jm666 In realtà sono 2 caratteri in più poiché normalmente si inserisce '<' oltre a uno spazio extra '' dopo <rispetto a un solo spazio se non si fosse utilizzato <:)
rasen58

2
@ rasen58 lo spazio è anche un personaggio? :) / okay,
sto

1
@duhaime ovviamente, se qualcuno deve fare ottimizzazioni. Ma IMHO per i problemi "comuni" è ok e la differenza è impercettibile. Inoltre, il head/ tailnon risolve lo sed -n '1p;3p'scenario - ovvero stampa più righe non adiacenti ...
jm666

1
@duhaime ovviamente - la nota è corretta e necessaria. :)
jm666

93

Ho una situazione unica in cui posso confrontare le soluzioni proposte in questa pagina e quindi sto scrivendo questa risposta come un consolidamento delle soluzioni proposte con tempi di esecuzione inclusi per ciascuna.

Impostare

Ho un file di dati di testo ASCII da 3.261 gigabyte con una coppia chiave-valore per riga. Il file contiene 3.339.550.320 righe in totale e sfida l'apertura in qualsiasi editor che ho provato, incluso il mio go-to Vim. Ho bisogno di sottoinsieme questo file al fine di indagare su alcuni dei valori che ho scoperto iniziano solo intorno alla riga ~ 500.000.000.

Perché il file ha così tante righe:

  • Devo estrarre solo un sottoinsieme delle righe per fare qualcosa di utile con i dati.
  • Leggere ogni riga che porta ai valori a cui tengo richiederà molto tempo.
  • Se la soluzione legge oltre le righe che mi interessano e continua a leggere il resto del file, perderà tempo a leggere quasi 3 miliardi di righe irrilevanti e impiegherà 6 volte più del necessario.

Il mio scenario migliore è una soluzione che estrae solo una singola riga dal file senza leggere nessuna delle altre righe nel file, ma non riesco a pensare a come lo farei in Bash.

Ai fini della mia sanità mentale non cercherò di leggere tutte le 500.000.000 di righe di cui avrei bisogno per il mio problema. Invece cercherò di estrarre la riga 50.000.000 su 3.339.550.320 (il che significa che la lettura del file completo richiederà 60 volte più tempo del necessario).

Userò il timebuilt-in per confrontare ogni comando.

Baseline

Prima vediamo come head tail soluzione:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

La linea di base per la riga 50 milioni è 00: 01: 15.321, se fossi andato dritto per la riga 500 milioni sarebbe probabilmente ~ 12,5 minuti.

taglio

Ne dubito, ma vale la pena provare:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

Questo ha richiesto 00: 05: 12.156 per l'esecuzione, che è molto più lento della linea di base! Non sono sicuro che abbia letto l'intero file o solo fino a 50 milioni prima di interrompere, ma a prescindere da ciò non sembra una soluzione praticabile al problema.

AWK

Ho eseguito la soluzione solo con il exitperché non avrei aspettato l'esecuzione del file completo:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

Questo codice è stato eseguito in 00: 01: 16.583, che è solo ~ 1 secondo più lento, ma non è ancora un miglioramento sulla base. A questo ritmo, se il comando exit fosse stato escluso, probabilmente ci sarebbero voluti circa 76 minuti per leggere l'intero file!

Perl

Ho eseguito anche la soluzione Perl esistente:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

Questo codice è stato eseguito in 00: 01: 13.146, che è ~ 2 secondi più veloce della linea di base. Se lo avessi eseguito su 500.000.000 in totale, probabilmente ci sarebbero voluti circa 12 minuti.

sed

La risposta migliore alla lavagna, ecco il mio risultato:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

Questo codice è stato eseguito in 00: 01: 12.705, che è 3 secondi più veloce della linea di base e ~ 0,4 secondi più veloce di Perl. Se l'avessi eseguito su tutte le 500.000.000 di file, probabilmente ci sarebbero voluti circa 12 minuti.

mapfile

Ho bash 3.1 e quindi non posso testare la soluzione mapfile.

Conclusione

Sembra che, per la maggior parte, sia difficile migliorare la head tailsoluzione. Nella migliore delle ipotesi la sedsoluzione fornisce un aumento dell'efficienza del ~ 3%.

(percentuali calcolate con la formula % = (runtime/baseline - 1) * 100)

Fila 50.000.000

  1. 00: 01: 12.705 (-00: 00: 02.616 = -3.47%) sed
  2. 00: 01: 13.146 (-00: 00: 02.175 = -2,89%) perl
  3. 00: 01: 15.321 (+00: 00: 00.000 = + 0,00%) head|tail
  4. 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%) awk
  5. 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%) cut

Fila 500.000.000

  1. 00: 12: 07.050 (-00: 00: 26.160) sed
  2. 00: 12: 11.460 (-00: 00: 21.750) perl
  3. 00: 12: 33.210 (+00: 00: 00.000) head|tail
  4. 00: 12: 45.830 (+00: 00: 12.620) awk
  5. 00: 52: 01.560 (+00: 40: 31.650) cut

Fila 3.338.559.320

  1. 01: 20: 54.599 (-00: 03: 05.327) sed
  2. 01: 21: 24.045 (-00: 02: 25.227) perl
  3. 01: 23: 49.273 (+00: 00: 00.000) head|tail
  4. 01: 25: 13.548 (+00: 02: 35.735) awk
  5. 05: 47: 23.026 (+04: 24: 26.246) cut

4
Mi chiedo quanto tempo impiegherebbe a inserire l'intero file in / dev / null. (E se questo fosse solo un benchmark del disco rigido?)
sanmai

Sento il bisogno perverso di inchinarmi alla tua proprietà di un dizionario di file di testo da 3+ concerti. Qualunque sia la logica, questo abbraccia la testualità :)
Stabledog

51

Con awkesso è abbastanza veloce:

awk 'NR == num_line' file

Quando questo è vero, il comportamento predefinito di awkviene eseguita: {print $0}.


Versioni alternative

Se il tuo file risulta essere enorme, è meglio exitdopo aver letto la riga richiesta. In questo modo risparmi il tempo della CPU Vedi il confronto dei tempi alla fine della risposta .

awk 'NR == num_line {print; exit}' file

Se vuoi dare il numero di riga da una variabile bash puoi usare:

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

Scopri quanto tempo viene risparmiato utilizzando exit, specialmente se la linea si trova nella prima parte del file:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

Quindi la differenza è 0.198s contro 1.303s, circa 6 volte più veloce.


Questo metodo sarà sempre più lento perché awk tenta di eseguire la divisione dei campi. Il sovraccarico della divisione del campo può essere ridotto diawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour

Il vero potere di awk in questo metodo viene fuori quando si desidera concatenare linea n1 di file1, n2 di file2, n3 o file3 ... awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3. Con GNU awk questo può essere velocizzato usando awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3.
kvantour,

@kvantour, in effetti, il prossimo file di GNU awk è ottimo per queste cose. Come mai si FS=RSevita la divisione del campo?
fedorqui "SO smettere di danneggiare"

1
FS=RSnon evita la divisione dei campi, ma analizza solo i $ 0 e assegna un solo campo perché non c'è RSin$0
kvantour

@kvantour Ho fatto alcuni test con FS=RSe non ho visto differenze sui tempi. Che ne dici di farmi una domanda al riguardo in modo da poter espandere? Grazie!
fedorqui "SO smettere di danneggiare"

29

Secondo i miei test, in termini di prestazioni e leggibilità la mia raccomandazione è:

tail -n+N | head -1

Nè il numero di riga desiderato. Ad esempio, tail -n+7 input.txt | head -1stamperà la settima riga del file.

tail -n+Nstamperà tutto a partire dalla riga Ne head -1la fermerà dopo una riga.


L'alternativa head -N | tail -1è forse leggermente più leggibile. Ad esempio, questo stamperà la settima riga:

head -7 input.txt | tail -1

Quando si tratta di prestazioni, non c'è molta differenza per le dimensioni più piccole, ma sarà sovraperformato dal tail | head(dall'alto) quando i file diventano enormi.

Il più votato sed 'NUMq;d'è interessante da sapere, ma direi che sarà compreso da un minor numero di persone fuori dalla scatola rispetto alla soluzione testa / coda ed è anche più lento di coda / testa.

Nei miei test, entrambe le versioni di code / teste hanno sovraperformato sed 'NUMq;d' costantemente. Ciò è in linea con gli altri parametri di riferimento pubblicati. È difficile trovare un caso in cui la coda / la testa fossero davvero cattive. Non è inoltre sorprendente, poiché si tratta di operazioni che ci si aspetterebbe di essere fortemente ottimizzate in un moderno sistema Unix.

Per avere un'idea delle differenze di prestazioni, questi sono i numeri che ottengo per un file enorme (9.3G):

  • tail -n+N | head -1: 3,7 sec
  • head -N | tail -1: 4,6 secondi
  • sed Nq;d: 18,8 sec

I risultati possono differire, ma le prestazioni head | tailed tail | headè, in generale, comparabile per input più piccoli, ed sedè sempre più lenta di un fattore significativo (circa 5x circa).

Per riprodurre il mio benchmark, puoi provare quanto segue, ma tieni presente che creerà un file 9.3G nella directory di lavoro corrente:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

Ecco l'output di una corsa sulla mia macchina (ThinkPad X1 Carbon con un SSD e 16G di memoria). Presumo che nella corsa finale tutto verrà dalla cache, non dal disco:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s

1
Le prestazioni sono diverse tra head | tailvs tail | head? O dipende da quale riga viene stampata (inizio del file vs fine del file)?
Wisbucky

1
@wisbucky Non ho dati concreti, ma uno svantaggio del primo utilizzo della coda seguito da una "testa -1" è che devi conoscere in anticipo la lunghezza totale. Se non lo conosci, dovresti prima contarlo, il che sarà una perdita in termini di prestazioni. Un altro svantaggio è che è meno intuitivo da usare. Ad esempio, se hai i numeri da 1 a 10 e vuoi ottenere la 3a riga, dovresti usare "tail -8 | head -1". Questo è più soggetto a errori di "head -3 | tail -1".
Philipp Claßen,

scusate, avrei dovuto includere un esempio per essere chiari. head -5 | tail -1vs tail -n+5 | head -1. In realtà, ho trovato un'altra risposta che ha fatto un confronto di prova e ho scoperto tail | headdi essere più veloce. stackoverflow.com/a/48189289
wisbucky

1
@wisbucky Grazie per averlo menzionato! Ho fatto alcuni test e devo ammettere che è stato sempre leggermente più veloce, indipendentemente dalla posizione della linea da quello che ho visto. Detto questo, ho cambiato la mia risposta e incluso anche il benchmark nel caso in cui qualcuno volesse riprodurlo.
Philipp Claßen,

27

Caspita, tutte le possibilità!

Prova questo:

sed -n "${lineNum}p" $file

o uno di questi a seconda della versione di Awk:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

( Potrebbe essere necessario provare il comando nawkogawk ).

Esiste uno strumento che stampa solo quella particolare linea? Nessuno degli strumenti standard. Tuttavia, sedè probabilmente il più vicino e il più semplice da usare.



21

Questa domanda essendo taggata Bash, ecco il modo di fare Bash (≥4): usare mapfilecon l' opzione -s(salta) e -n(conta).

Se è necessario ottenere la 42a riga di un file file:

mapfile -s 41 -n 1 ary < file

A questo punto, avrai una matrice aryi cui campi contengono le righe di file(inclusa la nuova riga finale), dove abbiamo saltato le prime 41 righe ( -s 41) e ci siamo fermati dopo aver letto una riga ( -n 1). Quindi questa è davvero la 42a linea. Per stamparlo:

printf '%s' "${ary[0]}"

Se hai bisogno di un intervallo di linee, dì l'intervallo 42–666 (incluso) e di non voler fare la matematica da solo, e stampale su stdout:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

Se è necessario elaborare anche queste righe, non è davvero conveniente memorizzare la riga finale trascinata. In questo caso utilizzare l' -topzione (trim):

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

Puoi avere una funzione farlo per te:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

Nessun comando esterno, solo built-in di Bash!


11

Puoi anche usare sed print ed uscire:

sed -n '10{p;q;}' file   # print line 10

6
L' -nopzione disabilita l'azione predefinita per stampare ogni riga, come sicuramente avresti scoperto con una rapida occhiata alla pagina man.
Tripleee

In GNU sed tutte le sedrisposte sono alla stessa velocità. Pertanto (per GNU sed ) questa è la sedrisposta migliore , dal momento che risparmierebbe tempo per file di grandi dimensioni e valori di riga n piccoli .
agc,

7

Puoi anche usare Perl per questo:

perl -wnl -e '$.== NUM && print && exit;' some.file

6

La soluzione più veloce per file di grandi dimensioni è sempre tail | head, a condizione che le due distanze:

  • dall'inizio del file alla linea di partenza. ChiamiamoloS
  • la distanza dall'ultima riga alla fine del file. Be itE

sono conosciuti. Quindi, potremmo usare questo:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

quanto è necessario solo il conteggio delle righe.

Alcuni dettagli in più in https://unix.stackexchange.com/a/216614/79743


1
Si prega di chiarire le unità di Se E, (cioè byte, caratteri o righe).
agc,

6

Tutte le risposte di cui sopra rispondono direttamente alla domanda. Ma ecco una soluzione meno diretta ma un'idea potenzialmente più importante, per provocare il pensiero.

Poiché le lunghezze di riga sono arbitrarie, è necessario leggere tutti i byte del file prima dell'ennesima riga . Se hai un file enorme o hai bisogno di ripetere questo compito molte volte e questo processo richiede molto tempo, allora dovresti seriamente pensare se dovresti archiviare i tuoi dati in un modo diverso in primo luogo.

La vera soluzione è avere un indice, ad esempio all'inizio del file, che indica le posizioni in cui iniziano le linee. È possibile utilizzare un formato di database o semplicemente aggiungere una tabella all'inizio del file. In alternativa, crea un file indice separato per accompagnare il tuo file di testo di grandi dimensioni.

ad es. potresti creare un elenco di posizioni dei personaggi per le nuove linee:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

quindi leggi con tail, che in realtà seekè direttamente al punto appropriato nel file!

ad es. per ottenere la linea 1000:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
  • Questo potrebbe non funzionare con caratteri a 2 byte / multibyte, poiché awk è "sensibile ai caratteri" ma coda no.
  • Non l'ho provato su un file di grandi dimensioni.
  • Vedi anche questa risposta .
  • In alternativa, dividi il tuo file in file più piccoli!

5

Come seguito alla molto utile risposta di benchmarking di CaffeineConnoisseur ... Ero curioso di sapere quanto velocemente il metodo "mapfile" è stato confrontato con gli altri (dato che non è stato testato), quindi ho provato io stesso un confronto veloce e sporco come Ho bash 4 a portata di mano. Ho lanciato un test del metodo "tail | head" (piuttosto che head | tail) menzionato in uno dei commenti sulla risposta in alto mentre ero lì, mentre la gente sta cantando le sue lodi. Non ho nulla di quasi le dimensioni del file di test utilizzato; il migliore che ho trovato in breve tempo è stato un file genealogico di 14 milioni (lunghe righe separate da spazi bianchi, poco meno di 12000 righe).

Versione breve: mapfile appare più veloce del metodo di taglio, ma più lento di tutto il resto, quindi lo definirei un vero disastro. coda | head, OTOH, sembra che potrebbe essere il più veloce, anche se con un file di queste dimensioni la differenza non è poi così sostanziale rispetto a sed.

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

Spero che sia di aiuto!


4

Usando ciò che altri hanno menzionato, volevo che fosse una funzione rapida e dandy nella mia shell bash.

Crea un file: ~/.functions

Aggiungi ad esso il contenuto:

getline() { line=$1 sed $line'q;d' $2 }

Quindi aggiungi questo al tuo ~/.bash_profile:

source ~/.functions

Ora quando apri una nuova finestra bash, puoi semplicemente chiamare la funzione in questo modo:

getline 441 myfile.txt


3

Se hai più righe delimitate da \ n (normalmente nuova riga). Puoi anche usare 'cut':

echo "$data" | cut -f2 -d$'\n'

Otterrai la seconda riga dal file. -f3ti dà la terza linea.


1
Può essere utilizzato anche per visualizzare più righe: cat FILE | cut -f2,5 -d$'\n'visualizzerà le righe 2 e 5 del FILE. (Ma non preserverà l'ordine.)
Andriy Makukha,

2

Per stampare l'ennesima riga usando sed con una variabile come numero di riga:

a=4
sed -e $a'q:d' file

Qui il flag '-e' serve per aggiungere lo script al comando da eseguire.


2
I due punti sono un errore di sintassi e dovrebbero essere un punto e virgola.
tripla il

2

Molte buone risposte già. Io personalmente vado con Awk. Per comodità, se usi bash, aggiungi quanto segue al tuo ~/.bash_profile. E la prossima volta che accedi (o se dai il tuo .bash_profile dopo questo aggiornamento), avrai una nuova "nth" funzione disponibile per reindirizzare i tuoi file.

Esegui questo o mettilo nel tuo ~ / .bash_profile (se usi bash) e riapri bash (o esegui source ~/.bach_profile)

# print just the nth piped in line nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }

Quindi, per usarlo, basta semplicemente attraversarlo. Per esempio,:

$ yes line | cat -n | nth 5 5 line


1

Dopo aver dato un'occhiata alla risposta migliore e al benchmark , ho implementato una piccola funzione di supporto:

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

Fondamentalmente puoi usarlo in due mode:

nth 42 myfile.txt
do_stuff | nth 42

0

Ho inserito alcune delle risposte sopra in un breve script bash che puoi inserire in un file chiamato get.she link /usr/local/bin/get(o qualunque altro nome tu preferisca).

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

Assicurati che sia eseguibile con

$ chmod +x get

Collegalo per renderlo disponibile sul PATHcon

$ ln -s get.sh /usr/local/bin/get

Consuma responsabilmente!

P

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.