Cum pot parcurge fiecare linie a unui fișier text cu Bash?
Cu acest script:
echo "Start!"
for p in (peptides.txt)
do
echo "${p}"
done
I a lua această ieșire pe ecran:
Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'
(Mai târziu am vrut să fac ceva mai complicat cu $p
decât doar de ieșire la ecran.)
Variabila de mediu SHELL este (de mediu):
SHELL=/bin/bash
/bin/bash --version
de ieșire:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
de ieșire:
Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006
Fișierul peptides.txt conține:
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
O modalitate de a face este:
while read p; do
echo "$p"
done <peptides.txt
După cum a subliniat în comentariile, acest lucru are efecte secundare de tundere de conducere spațiu, interpretarea backslash secvențe, și sărind peste ultima linie daca's lipsește o încheiere tractor. Dacă sunt nelămuriri, poți să faci:
while IFS="" read -r p || [ -n "$p" ]
do
printf '%s\n' "$p"
done < peptides.txt
În mod excepțional, în cazul în care corpul buclei poate citi de la intrarea standard, puteți deschide fișierul folosind un alt descriptor de fisier:
while read -u 10 p; do
...
done 10<peptides.txt
Aici, 10 este doar un număr arbitrar (diferit de 0, 1, 2).
cat peptides.txt | while read line
do
# do something with $line here
done
și-o linie varianta:
cat peptides.txt | while read line; do something_with_$line_here; done
Aceste opțiuni vor sări peste ultima linie a fișierului dacă nu există nici un sfârșit de linie de alimentare.
Puteți evita acest lucru prin următoarele:
cat peptides.txt | while read line || [[ -n $line ]];
do
# do something with $line here
done
Opțiunea 1a: în Timp ce bucla: o Singură linie la un moment dat: de Intrare de redirecționare
#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do
echo $p
done < $filename
Opțiunea 1b: în Timp ce bucla: o Singură linie la un moment dat: Deschideți fișierul, citite dintr-un descriptor de fișier (în acest caz, fișierul descriptor #4).
#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
echo $p
done
Acest lucru este nu mai bine decât alte răspunsuri, dar este o modalitate de a obține locuri de muncă făcut într-un fișier, fără spații (vezi comentarii). Mi se pare că eu de multe ori nevoie de unul-garnituri să sape prin liste în fișiere text fără pas suplimentar de a folosi separat fișiere script.
for word in $(cat peptides.txt); do echo $word; done
Acest format permite-mi să pun totul într-o singură linie de comandă. Schimba "echo $cuvinte" parte de tot ce vrei și poate emite mai multe comenzi separate prin punct și virgulă. Următorul exemplu utilizează fișierul's cuprins ca argumente în două alte script-uri poate ai scris.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done
Sau dacă intenționați să utilizați acest lucru ca un stream editor (aflați sed) te poate arunca de ieșire la un alt fișier, după cum urmează.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt
Am'am folosit aceste cum am scris mai sus pentru că mi-au utilizat fișierele de text în cazul în care nu've le-a creat cu un singur cuvânt pe linie. (Vezi comentarii) Dacă aveți spații care nu't doresc divizarea cuvinte/rânduri, devine un pic mai urat, dar aceeași comandă încă funcționează după cum urmează:
OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS
Asta doar spune shell pentru a împărți pe linii noi doar, nu spații, apoi revine mediului înapoi la ceea ce a fost anterior. În acest moment, poate doriți să ia în considerare pune totul într-un script de shell, mai degrabă decât de a strânge toate într-o singură linie, totuși.
Cel mai bun de noroc!
Câteva lucruri care nu sunt acoperite de alte răspunsuri:
# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
# process the fields
# if the line has less than three fields, the missing fields will be set to an empty string
# if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt
while read -r line; do
# process the line
done < <(command ...)
Această abordare este mai bună decât `comanda ... | în timp ce citesc -r linie; face ... pentru că în timp ce buclă aici se execută în shell-ul curent, mai degrabă decât un subshell ca în cazul din urmă. Vezi cele legate de post O variabilă modificată în interiorul o buclă în timp ce nu este amintit.
while read -r -d '' line; do
# logic
# use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)
Legate de citit: BashFAQ/020 - Cum pot găsi și în condiții de siguranță mâner nume de fișiere care conțin newline, spații sau ambele?
while read -u 3 -r line1 && read -u 4 -r line2; do
# process the lines
# note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt
Bazat pe @chepner's a raspuns aici:
-u
este un bash extensie. Pentru POSIX compatibilitate, fiecare apel va arata ceva de genul citit -r X <&3
.
while read -r line; do
my_array+=("$line")
done < my_file
Dacă fișierul se termină cu un incomplete linie (newline dispărut la sfârșitul anului), atunci:
while read -r line || [[ $line ]]; do
my_array+=("$line")
done < my_file
readarray -t my_array < my_file
sau
mapfile -t my_array < my_file
Și apoi
for line in "${my_array[@]}"; do
# process the lines
done
Legate de posturi:
Utilizați o buclă de timp, astfel:
while IFS= read -r line; do
echo "$line"
done <file
Note:
Dacă tu nu't setați FI
corect, veți pierde indentare.
Aproape întotdeauna trebuie să folosiți -r, cu opțiune de citit.
Să presupunem că aveți acest fișier:
$ cat /tmp/test.txt
Line 1
Line 2 has leading space
Line 3 followed by blank line
Line 5 (follows a blank line) and has trailing space
Line 6 has no ending CR
Există patru elemente care va schimba sensul de ieșire de fișier citit de multe Bash solutii:
Dacă doriți ca fișier text, linie cu linie, inclusiv linii goale și de încheiere linii fără CR, trebuie să utilizați o buclă de timp și trebuie să aibă o alternativă de testare pentru linia de final.
Aici sunt metodele care pot schimba de fișier (în comparație cu ceea ce "pisică" se întoarce):
$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
(Dacă o faci în timp ce, în IFS= read-r p; nu printf "%s\n" "'$p'"; face </tmp/test.txt` în schimb, să păstreze pe de conducere și la sfârșit spații, dar încă pierde în ultimul rând, dacă nu este încheiată cu CR)
$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
Line 2 has leading space
Line 3 followed by blank line
Line 5 (follows a blank line) and has trailing space
Line 6 has no ending CR'
(Dacă scoateți la " de la
$(cat /tmp/test.txt)` ai citit dosarul cuvânt cu cuvânt, mai degrabă decât o singură înghițitură. De asemenea, probabil, nu ceea ce este destinat...)
Cel mai robust și mai simplu mod de a citi un fișier linie cu linie și să păstreze toate spațierea este:
$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
' Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space '
'Line 6 has no ending CR'
Dacă doriți să se dezbrace de conducere și spații comerciale, scoateți IFS=
parte:
$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'
(Un fișier text fără încetare \n
, în timp ce destul de comun, este considerat a rupt sub POSIX. Dacă puteți conta pe final \n
nu trebuie || [[ -n $line]] " în " timp
bucla.)
Mai la BASH FAQ
Aici este viața mea reală exemplu cum la bucla linii de un alt program de ieșire, verificați pentru siruri de caractere, drop dublu citate de variabile, utilizarea ca variabilă în afara buclei. Cred că destul de mulți este să pui aceste întrebări, mai devreme sau mai târziu.
##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
echo ParseFPS $line
FPS=parse
fi
if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
echo ParseFPS $line
FPS=${line##*=}
FPS="${FPS%\"}"
FPS="${FPS#\"}"
fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then
echo ParseFPS Unknown frame rate
fi
echo Found $FPS
Declara variabila in afara buclei, setați valoarea și să-l utilizați în afara de buclă necesită face <<< "$(...)" sintaxa. Cererea trebuie să fi rulat într-un context de curent consola. Citate în jurul comanda păstrează liniile de flux de ieșire.
Bucla se potrivesc pentru siruri de caractere, apoi citește nume=valoare pereche, desparte-dreapta parte din ultima = caracter, picături primul citat, picături ultimul citat, avem o valoare curat pentru a fi utilizate în altă parte.
Asta vine destul de târziu, dar cu gândul că-l poate ajuta pe cineva, eu sunt adăugarea răspunsul. De asemenea, acest lucru nu poate fi cel mai bun mod. " șef "de comandă poate fi folosit cu -n
argument de a citi n linii la început de fișier și, de asemenea, "coada" de comandă poate fi folosit pentru a citi de jos. Acum, pentru a aduce n linie din fișier, am capul n linii, țeavă de date la coada doar 1 linie de conducte de date.
TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 `
echo $TOTAL_LINES # To validate total lines in the file
for (( i=1 ; i <= $TOTAL_LINES; i++ ))
do
LINE=`head -n$i $USER_FILE | tail -n1`
echo $LINE
done
@Peter: Acest lucru ar putea lucra pentru tine
echo "Start!";for p in $(cat ./pep); do
echo $p
done
Acest lucru se va întoarce la ieșire-
Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL