Ottieni una riga specifica dal file di testo usando solo lo script di shell


100

Sto cercando di ottenere una riga specifica da un file di testo.

Finora, online ho visto solo cose come sed, (posso usare solo sh-non bash o sed o qualcosa del genere). Ho bisogno di farlo solo utilizzando uno script di shell di base.

cat file | while read line
    do
       #do something
    done

So come scorrere le righe, come mostrato sopra, ma cosa succede se ho solo bisogno di ottenere il contenuto di una particolare riga


conosci il numero di riga?
Mehul Rathod

1
Allora devi contare.
Ignacio Vazquez-Abrams

sì, il numero di riga è 5 @MehulRathod
GangstaGraham

3
Perché va catbene ma sednon lo è? Non ha senso.
William Pursell

5
Perché nessuno può dire di no a cat. Aw ... carino cat!

Risposte:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

E con il comando sh, non posso usare sed, awk. Dovrei renderlo più chiaro nella domanda.
GangstaGraham

@GangstaGraham hai detto che sai come scorrere le linee, che ne dici di aggiungere un contatore? se il contatore raggiunge il numero di riga di destinazione, ottenere la riga e interrompere il ciclo. aiuta?
Kent

4
@KanagaveluSugumar ha letto la pagina informativa di sed. 5!dsignifica eliminare tutte le righe tranne 5. shell var è possibile, sono necessari doppi apici.
Kent

13
Suggerirei di aggiungere un'altra variante: sed -n 5pquesto sembra più logico da ricordare per i neofiti, perché -nsignifica "nessun output per impostazione predefinita" e psta per "stampa", e non c'è nessun accenno potenzialmente confuso all'eliminazione (quando si parla di file, l'eliminazione di righe tende a significa qualcosa di diverso).
Josip Rodin

1
@JosipRodin hai ragione, -n '5p'funziona anche per questo problema. La differenza qui è 5!dche puoi aggiungere -iper riscrivere la modifica nel file. comunque con -n 5pte devo sed -n '5p' f > f2&& mv f2 fancora, per questa domanda, sono d'accordo con la tua opinione.
Kent

21

Supponendo che linesia una variabile che contiene il numero di riga richiesto, se puoi usare heade tail, allora è abbastanza semplice:

head -n $line file | tail -1

In caso contrario, dovrebbe funzionare:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

Questo -eqconfronto è per interi, quindi vuole un numero di riga, non un contenuto di riga ( $line). Questo deve essere risolto definendo ad esempio want=5prima del ciclo, e quindi utilizzando il -eqconfronto su $want. [spostato da una modifica rifiutata]
Josip Rodin

1
@ JosipRodin Ho fatto un suggerimento di modifica indipendente in base al tuo commento, poiché sono d'accordo con esso. Si spera che questa volta non venga rifiutato.
Victor Zamanian

15

Potresti usare sed -n 5p file.

Puoi anche ottenere un intervallo, ad esempio sed -n 5,10p file.


11

Miglior metodo di prestazione

sed '5q;d' file

Perché sedsmette di leggere tutte le righe dopo la quinta

Aggiorna esperimento del Sig. Roger Dueck

Ho installato wcanadian-insane (6,6 MB) e ho confrontato sed -n 1p / usr / share / dict / words e sed '1q; d' / usr / share / dict / words usando il comando time; il primo ha impiegato 0,043 secondi, il secondo solo 0,002 secondi, quindi l'utilizzo di "q" è sicuramente un miglioramento delle prestazioni!


1
Questo è anche comunemente scritto:sed -n 5q
William Pursell

3
Mi piace questa soluzione perché sedsmette di leggere tutte le righe dopo la quinta.
Anthony Geoghegan

1
Ho installato wcanadian-insane (6,6 MB) e ho confrontato sed -n 1p /usr/share/dict/wordse sed '1q;d' /usr/share/dict/wordsutilizzando il timecomando; il primo ha impiegato 0,043 secondi, il secondo solo 0,002 secondi, quindi l'utilizzo di "q" è sicuramente un miglioramento delle prestazioni!
Roger Dueck

5

Se ad esempio vuoi ottenere le righe da 10 a 20 di un file puoi usare ciascuno di questi due metodi:

head -n 20 york.txt | tail -11

o

sed -n '10,20p' york.txt 

p nel comando sopra sta per la stampa.

Ecco cosa vedrai: inserisci qui la descrizione dell'immagine


2

Il modo standard per fare questo genere di cose è usare strumenti esterni. Non consentire l'uso di strumenti esterni durante la scrittura di uno script di shell è assurdo. Tuttavia, se davvero non vuoi usare strumenti esterni, puoi stampare la riga 5 con:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Notare che questo stamperà la riga logica 5. Cioè, se input-filecontiene continuazioni di riga, verranno conteggiate come una singola riga. È possibile modificare questo comportamento aggiungendo -ral comando di lettura. (Che è probabilmente il comportamento desiderato.)


1
$((++i))sembra essere un bashismo; se l'OP è limitato nell'uso di strumenti esterni, non presumo che avranno accesso a più di un semplice/bin/sh
Josip Rodin

@JosipRodin No, è una funzionalità POSIX (ma il supporto per gli ++incrementi è specificamente contrassegnato come opzionale).
tripla

@ tripleee non funziona con il trattino moderno come / bin / sh, quindi non ci farei affidamento.
Josip Rodin

Ma una soluzione semplice come $((i+=1))funziona anche in Dash.
tripla

$(($i+1))è la semplice soluzione alternativa a cui stavo pensando.
Josip Rodin

1

Parallelamente alla risposta di William Pursell , ecco un semplice costrutto che dovrebbe funzionare anche nella shell Bourne originale v7 (e quindi anche in luoghi in cui Bash non è disponibile).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Notare anche l'ottimizzazione per l' breakuscita dal ciclo quando abbiamo ottenuto la linea che stavamo cercando.


0

Non mi è piaciuta particolarmente nessuna delle risposte.

Ecco come l'ho fatto.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

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

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

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'ma smartmatch è sperimentale e il suo utilizzo è sconsigliato
Sorin

Nessuna delle altre soluzioni è così concisa o consente tanta flessibilità. (Perché sembra che tutto ciò che fa risparmiare tempo e semplifica le cose, è "scoraggiato" da "persone intelligenti", dovremmo tutti guardare gli schermi tutto il giorno?)
dagelf

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
potresti descrivere un po 'almeno il motivo di questo lavoro per renderlo più chiaro alla persona che ha posto la domanda?
ted

Quindi, il primo grep seleziona tutte le righe aggiungendo i numeri di riga all'inizio. Quindi il secondo grep seleziona una riga specifica facendo corrispondere il numero di riga all'inizio. Infine, il numero di riga viene tagliato dall'inizio della riga in eco.
Oder

Questo è sia complesso che inefficiente rispetto a sed -n 5p, che ovviamente può ancora essere ottimizzato per qualcosa comesed -n '5!d;p;q'
tripleee
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.