Zapętlanie zawartości pliku w Bash
Jak przejrzeć każdy wiersz pliku tekstowego za pomocą Bash?
Z tym skryptem:
echo "Start!"
for p in (peptides.txt)
do
echo "${p}"
done
Dostaję to wyjście na ekranie:
Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'
(później chcę zrobić coś bardziej skomplikowanego z $p
, niż tylko wyjście na ekran.)
Zmienna środowiskowa SHELL jest (z ENV):
SHELL=/bin/bash
/bin/bash --version
wyjście:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
wyjście:
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
Peptydy plików.txt zawiera:
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
11 answers
Jednym ze sposobów jest:
while read p; do
echo "$p"
done <peptides.txt
Jak wspomniano w komentarzach, ma to skutki uboczne przycinania wiodących białych znaków, interpretacji sekwencji ukośników wstecznych i pomijania końcowej linii, jeśli brakuje jej szybkości kończącej linię. Jeśli są to obawy, można zrobić:
while IFS="" read -r p || [ -n "$p" ]
do
printf '%s\n' "$p"
done < peptides.txt
Wyjątkowo, jeśli ciało pętli może odczytywać ze standardowego wejścia , możesz otworzyć plik używając innego deskryptora pliku:
while read -u 10 p; do
...
done 10<peptides.txt
Tutaj 10 to tylko dowolna liczba (Inna od 0, 1, 2).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 20:55:56
cat peptides.txt | while read line
do
# do something with $line here
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 17:54:38
Option 1a: While loop: Single line at a time: input redirection
#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do
echo $p
done < $filename
Opcja 1b: pętla While: pojedyncza linia na raz:
Otwórz plik, odczytany z deskryptora pliku (w tym przypadku deskryptor pliku #4).
#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
echo $p
done
Opcja 2: For loop: wczytuje plik do pojedynczej zmiennej i analizuje.
Składnia ta będzie przetwarzać "linie" na podstawie dowolnych białych spacji między tokenami. To nadal działa, ponieważ podane linie plików wejściowych są jednowyrazowymi tokenami. Jeśli było więcej niż jeden token na linię, wtedy ta metoda nie będzie działać. Ponadto odczyt pełnego pliku do pojedynczej zmiennej nie jest dobrą strategią dla dużych plików.
#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
echo $line
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-06 10:42:03
To nie jest lepsze niż inne odpowiedzi, ale jest jeszcze jeden sposób na wykonanie zadania w pliku bez spacji (patrz komentarze). Uważam, że często potrzebuję one-linerów, aby przekopywać się przez listy w plikach tekstowych bez dodatkowego kroku używania oddzielnych plików skryptów.
for word in $(cat peptides.txt); do echo $word; done
Ten format pozwala mi umieścić to wszystko w jednym wierszu poleceń. Zmień część "echo $word" na dowolną i możesz wydać wiele poleceń oddzielonych średnikami. Poniższy przykład wykorzystuje zawartość pliku jako argumenty do dwóch innych skryptów, które mogłeś napisać.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done
Lub jeśli zamierzasz używać tego jak edytor strumienia (learn sed), możesz zrzucić wyjście do innego pliku w następujący sposób.
for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt
Użyłem ich tak, jak napisano powyżej, ponieważ użyłem plików tekstowych, w których utworzyłem je z jednym słowem w linii. (Zobacz komentarze) Jeśli masz spacje, których nie chcesz dzielić słów/linii, robi się to trochę brzydsze, ale to samo polecenie nadal działa w następujący sposób:
OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS
To po prostu mówi powłoce, aby dzieliła się tylko na nowe linie, a nie na spacje, a następnie zwraca środowisko z powrotem do tego, co było wcześniej. W tym momencie możesz rozważyć umieszczenie tego wszystkiego w skrypcie powłoki, a nie wyciskanie go w jednej linii.
Powodzenia!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-22 15:47:48
Użyj pętli while, tak:
while IFS= read -r line; do
echo "$line"
done <file
Uwagi:
Jeśli nie ustawisz
IFS
poprawnie, utracisz wcięcia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-29 00:10:24
Jeszcze kilka rzeczy nie objętych innymi odpowiedziami:
Odczyt z rozdzielonego pliku
# ':' 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
Odczyt z wyjścia innego polecenia, przy użyciu podstawienia procesu
while read -r line; do
# process the line
done < <(command ...)
To podejście jest lepsze niż command ... | while read -r line; do ...
ponieważ pętla while działa w bieżącej powłoce, a nie w subshell, jak w przypadku tej drugiej. Zobacz related post zmienna zmodyfikowana wewnątrz pętli while nie jest pamiętana .
Odczyt z ograniczonego wejścia null, dla przykład find ... -print0
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)
Related read: BashFAQ / 020-Jak mogę znaleźć i bezpiecznie obsługiwać nazwy plików zawierające nowe linie, spacje lub oba?
Odczyt z więcej niż jednego pliku na raz
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
Na podstawie @chepner ' s odpowiedz tutaj :
-u
jest rozszerzeniem bash. Dla zgodności z POSIX, każde wywołanie będzie wyglądało jak read -r X <&3
.
Odczyt całego pliku do tablicy (wersje Bash wcześniejsze do 4)
while read -r line; do
my_array+=("$line")
done < my_file
Jeśli plik kończy się niepełną linią (na końcu brakuje nowej linii), a następnie:
while read -r line || [[ $line ]]; do
my_array+=("$line")
done < my_file
Odczyt całego pliku do tablicy (wersje Bash 4x i nowsze)
readarray -t my_array < my_file
Lub
mapfile -t my_array < my_file
A potem
for line in "${my_array[@]}"; do
# process the lines
done
Więcej o komend zbudowanych w powłoce
read
ireadarray
- GNU- BashFAQ / 001-Jak mogę odczytać plik (strumień danych, zmienna) linia po linii (i / lub pole po polu)?
Related posts:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 20:38:35
Jeśli nie chcesz, aby odczyt był łamany przez znak nowej linii, użyj -
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
done < "$1"
Następnie uruchom skrypt z nazwą pliku jako parametrem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-08 16:10:51
Załóżmy, że masz ten plik:
$ 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
Istnieją cztery elementy, które zmienią znaczenie pliku wyjściowego odczytywanego przez wiele rozwiązań Bash:
- pusta linia 4;
- spacje wiodące lub końcowe na dwóch liniach;
- zapisywanie wartości poszczególnych wierszy (tj. każdy wiersz jest rekordem);
- linia 6 nie zakończona CR.
Jeśli chcesz, aby plik tekstowy linia po linii, w tym puste linie i kończące linie bez CR, musisz użyć pętli while i musisz mieć alternatywny test dla ostatniej linii.
Oto metody, które mogą zmienić plik (w porównaniu do tego, co zwraca cat
):
1) traci ostatnią linię oraz spacje wiodące i końcowe:
$ 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'
(jeśli zamiast tego wykonasz while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
, zachowasz początkowe i końcowe spacje, ale nadal stracisz ostatnią linię, jeśli nie zostanie zakończona CR)
2) użycie substytucji procesu za pomocą cat
spowoduje odczytanie całego pliku jednym łykiem i traci znaczenie poszczególnych wierszy:
$ 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'
(jeśli usuniesz "
z $(cat /tmp/test.txt)
, odczytasz plik słowo po słowie, a nie jeden łyk. Również prawdopodobnie nie to, co jest zamierzone...)
Najbardziej solidnym i najprostszym sposobem odczytu pliku linia po linii i zachowania wszystkich odstępów jest:
$ 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'
Jeśli chcesz usunąć wiodące i handlowe przestrzenie, Usuń IFS=
część:
$ 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'
(plik tekstowy bez zakończenia \n
, choć dość powszechny, uważany jest za uszkodzony pod POSIX. Jeśli możesz liczyć na końcowe \n
, nie potrzebujesz || [[ -n $line ]]
W pętli while
.)
Więcej w Bash FAQ
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-02 17:17:53
#!/bin/bash
#
# Change the file name from "test" to desired input file
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
echo $x
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-24 17:57:03
Oto mój prawdziwy przykład, jak zapętlić linie innego wyjścia programu, sprawdzić podciągi, zrzucić podwójne cudzysłowy ze zmiennej, użyć tej zmiennej poza pętlą. Myślę, że dość wielu zadaje te pytania prędzej czy później.
##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
Zadeklaruj zmienną poza pętlą, ustaw wartość i użyj jej poza pętlą wymaga zrobione składnia. Aplikacja musi być uruchamiana w kontekście bieżącej konsoli. Cudzysłowy wokół polecenia zachowują nowe linie wyjścia strumień.
Loop match dla podłańcuchów odczytuje nazwa = wartość para, dzieli prawą część ostatniego = znak, krople pierwszy cytat, krople ostatni cytat, mamy czystą wartość do użycia gdzie indziej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 08:15:45
@Piotr: to może Ci się udać-
echo "Start!";for p in $(cat ./pep); do
echo $p
done
To zwróci Wyjście -
Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 05:00:05