Jak iterować każdą linię pliku tekstowego za pomocą Bash ?
Za pomocą tego skryptu:
echo "Start!"
for p in (peptides.txt)
do
echo "${p}"
done
Otrzymuję ten wynik 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 $p
niż tylko wyświetlanie na ekranie.)
Zmienna środowiskowa SHELL to (z env):
SHELL=/bin/bash
/bin/bash --version
wynik:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
wynik:
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
Plik peptides.txt zawiera:
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
Odpowiedzi:
Jednym ze sposobów na to jest:
Jak wskazano w komentarzach, ma to skutki uboczne przycinania wiodących białych znaków, interpretowania sekwencji odwrotnego ukośnika i pomijania ostatniego wiersza, jeśli brakuje końca linii. Jeśli są to obawy, możesz:
Wyjątkowo, jeśli ciało pętli może czytać ze standardowego wejścia , możesz otworzyć plik przy użyciu innego deskryptora pliku:
Tutaj 10 jest tylko dowolną liczbą (różną od 0, 1, 2).
źródło
while read p || [[ -n $p ]]; do ...
oraz wariant jednowarstwowy:
Te opcje pomijają ostatni wiersz pliku, jeśli nie ma końca wiersza końcowego.
Możesz tego uniknąć, wykonując następujące czynności:
źródło
Opcja 1a: Pętla while: Pojedyncza linia na raz: Przekierowanie wejścia
Opcja 1b: Pętla while: Pojedyncza linia na raz:
Otwórz plik, czytaj z deskryptora pliku (w tym przypadku deskryptor pliku # 4).
źródło
done < $filename
jądone 4<$filename
(co jest przydatne, jeśli chcesz odczytać nazwę pliku z parametru polecenia, w którym to przypadku możesz po prostu zastąpić$filename
przez$1
).tail -n +2 myfile.txt | grep 'somepattern' | cut -f3
podczas uruchamiania poleceń ssh wewnątrz pętli (zużywa standardowe wejście); opcja 2 wydaje się tutaj jedynym sposobem?Nie jest to lepsze niż inne odpowiedzi, ale jest jeszcze jednym sposobem na wykonanie pracy w pliku bez spacji (patrz komentarze). Uważam, że często potrzebuję jednowierszowych, aby przeglądać listy w plikach tekstowych bez dodatkowego etapu korzystania z oddzielnych plików skryptów.
Ten format pozwala mi umieścić wszystko w jednym wierszu poleceń. Zmień część „echo $ słowo” na dowolną, a możesz wydawać wiele poleceń oddzielonych średnikami. W poniższym przykładzie użyto zawartości pliku jako argumentów dwóch innych skryptów, które mogłeś napisać.
Lub jeśli zamierzasz używać tego jak edytora strumieniowego (naucz się sed), możesz zrzucić dane wyjściowe do innego pliku w następujący sposób.
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 wierszu. (Patrz komentarze) Jeśli masz spacje, których nie chcesz dzielić słów / linii, robi się to trochę brzydsze, ale to samo polecenie działa w następujący sposób:
To po prostu mówi powłoce, by dzieliła się tylko na znakach nowej linii, a nie na spacje, a następnie przywraca środowisko do poprzedniego stanu. W tym momencie możesz rozważyć umieszczenie tego wszystkiego w skrypcie powłoki zamiast ściśnięcia go w jednym wierszu.
Powodzenia!
źródło
for
powoduje, że tokeny / linie wejściowe podlegają rozszerzeniom powłoki, co zwykle jest niepożądane; spróbuj tego:for l in $(echo '* b c'); do echo "[$l]"; done
- jak zobaczysz*
- mimo że pierwotnie cytowany literał - rozwija się do plików w bieżącym katalogu.for
iteracja linii plików jest złym pomysłem. Plus aspekt rozszerzenia wspomniany przez @ mklement0 (nawet jeśli prawdopodobnie można go obejść, wprowadzając znaki ucieczki, co ponownie sprawia, że rzeczy są bardziej złożone i mniej czytelne).Kilka innych rzeczy nieobjętych innymi odpowiedziami:
Odczytywanie z pliku rozdzielanego
Odczytywanie z wyjścia innego polecenia, z wykorzystaniem podstawiania procesów
To podejście jest lepsze niż
command ... | while read -r line; do ...
dlatego, że pętla while działa tutaj w bieżącej powłoce, a nie w podpowłoce, jak w przypadku tej ostatniej. Zobacz powiązany post Zmienna zmodyfikowana w pętli while nie jest zapamiętywana .Na przykład odczyt z danych rozdzielanych znakami zerowymi
find ... -print0
Powiązana lektura: BashFAQ / 020 - Jak znaleźć i bezpiecznie obsługiwać nazwy plików zawierające znaki nowej linii, spacje lub oba?
Odczytywanie z więcej niż jednego pliku na raz
Na podstawie @ chepner za odpowiedź tutaj :
-u
jest rozszerzeniem bash. Dla zgodności z POSIX każde połączenie wyglądałoby mniej więcej takread -r X <&3
.Odczytywanie całego pliku do tablicy (wersje Bash wcześniejsze niż 4)
Jeśli plik kończy się niepełną linią (na końcu brakuje nowej linii), to:
Odczytywanie całego pliku do tablicy (wersje Bash 4x i nowsze)
lub
I wtedy
Więcej o wbudowanych powłokach
read
ireadarray
poleceniach - GNUWięcej o
IFS
- WikipediaPowiązane posty:
źródło
command < input_filename.txt
ciebie zawsze możesz zrobićinput_generating_command | command
lubcommand < <(input_generating_command)
Użyj pętli while, tak jak to:
Uwagi:
Jeśli nie ustawisz
IFS
poprawnie, utracisz wcięcie.Prawie zawsze powinieneś używać opcji -r z poleceniem read.
Nie czytaj wierszy za pomocą
for
źródło
-r
opcja?Note #2
to link, w którym jest szczegółowo opisany ...-u
opcji, czy mówisz o innym przykładzie-u
?Załóżmy, że masz ten plik:
Istnieją cztery elementy, które zmienią znaczenie danych wyjściowych pliku odczytanych przez wiele rozwiązań Bash:
Jeśli chcesz, aby plik tekstowy linia po linii obejmował puste linie i linie końcowe bez CR, musisz użyć pętli while i mieć alternatywny test dla ostatniej linii.
Oto metody, które mogą zmienić plik (w porównaniu do tego, co
cat
zwraca):1) Strać ostatnią linię oraz spacje wiodące i końcowe:
(Jeśli to zrobisz
while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
, zachowujesz początkowe i końcowe spacje, ale nadal tracisz ostatni wiersz, jeśli nie jest on zakończony CR)2) Użycie substytucji procesu
cat
spowoduje odczytanie całego pliku w jednym łyku i utratę znaczenia poszczególnych wierszy:(Jeśli usuniesz
"
z$(cat /tmp/test.txt)
, przeczytasz plik słowo po słowie zamiast jednego łyka. Prawdopodobnie też nie to, co jest zamierzone ...)Najbardziej niezawodny i najprostszy sposób na odczytanie pliku wiersz po wierszu i zachowanie wszystkich odstępów:
Jeśli chcesz usunąć przestrzenie wiodące i handlowe, usuń
IFS=
część:(Plik tekstowy bez zakończenia
\n
, choć dość powszechny, jest uważany za uszkodzony w POSIX. Jeśli możesz liczyć na końcowe\n
, nie potrzebujesz|| [[ -n $line ]]
wwhile
pętli.)Więcej na stronie BASH FAQ
źródło
Jeśli nie chcesz, aby twój odczyt był przerywany znakiem nowej linii, użyj -
Następnie uruchom skrypt z nazwą pliku jako parametrem.
źródło
źródło
Oto mój przykład z życia, w jaki sposób zapętlać linie innego wyjścia programu, sprawdzać podłańcuchy, upuszczać podwójne cudzysłowy ze zmiennej, używać tej zmiennej poza pętlą. Wydaje mi się, że całkiem sporo prędzej czy później zadaje te pytania.
Deklaracja zmiennej poza pętlą, ustawienie wartości i użycie jej poza pętlą wymaga ukończonej składni <<< „$ (...)” . Aplikację należy uruchomić w kontekście bieżącej konsoli. Cudzysłowy wokół polecenia zachowują nowe linie strumienia wyjściowego.
Dopasowanie pętli dla podciągów następnie odczytuje parę nazwa = wartość , dzieli prawą część ostatniego = znak, upuszcza pierwszy cytat, upuszcza ostatni cytat, mamy czystą wartość do użycia w innym miejscu.
źródło
Nadchodzi dość późno, ale z myślą, że może komuś pomóc, dodam odpowiedź. Może to nie być najlepszy sposób.
head
można użyć polecenia z-n
argumentem do odczytu n wierszy od początku pliku, podobnietail
można użyć polecenia do odczytu od dołu. Teraz, aby pobrać n-tą linię z pliku, kierujemy n liniami , potokujemy dane do końca tylko 1 linię z danych potokowych.źródło
sed
lubhead
+tail
jest niezwykle nieefektywne i oczywiście nasuwa się pytanie, dlaczego po prostu nie używasz jednego z innych rozwiązań tutaj. Jeśli potrzebujesz znać numer linii, dodaj licznik dowhile read -r
pętli lub użyj,nl -ba
aby dodać prefiks numeru linii do każdej linii przed pętlą.@Peter: To może Ci pomóc
To zwróci wynik
źródło