Poniższe wątki na tej stronie i StackOverflow były pomocne w zrozumieniu, jak IFS
działa:
- Czym jest IFS w kontekście pętli?
- Jak zapętlić linie wiersza pliku
- Bash, czytaj wiersz po wierszu z pliku, za pomocą IFS
Ale wciąż mam krótkie pytania. Postanowiłem zapytać ich w tym samym poście, ponieważ uważam, że może to pomóc przyszłym czytelnikom:
Pytanie 1 IFS
jest zwykle omawiane w kontekście „podziału pola”. Czy podział pola jest taki sam jak podział słów ?
Q2: Specyfikacja POSIX mówi :
Jeżeli wartość IFS jest null, podział pola nie będzie wykonywany.
Czy ustawienie jest IFS=
takie samo jak ustawienie IFS
na null? Czy to też oznacza ustawienie go empty string
również na?
P3: W specyfikacji POSIX czytam :
Jeśli IFS nie jest ustawiony, powłoka zachowuje się tak, jakby wartość IFS wynosiła
<space>, <tab> and <newline>
Powiedz, że chcę przywrócić domyślną wartość IFS
. W jaki sposób mogę to zrobić? (dokładniej, jak mam się odwoływać <tab>
i <newline>
?)
P4: Wreszcie, w jaki sposób ten kod:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
zachowaj się, jeśli zmienimy pierwszą linię na
while read -r line # Use the default IFS value
lub:
while IFS=' ' read -r line
IFS
i rozbrojoneIFS
są bardzo różne. Odpowiedź na czwarty kwartał jest częściowo błędna: nie dotykano tutaj wewnętrznych separatorów, tylko wiodące i końcowe.IFS
, wszystkie oznaczająIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Eee, co? Powinno tam być wiele separatorów spacji, silnik SO ciągle je rozbiera).read
; ostatnia zmienna pobiera wszystko, co zostało, z wyjątkiem ostatniego separatora i pozostawia wewnętrzne separatory w środku.P1: Tak. „Podział na pola” i „podział na słowa” to dwa terminy dla tej samej koncepcji.
P2: Tak. Jeśli
IFS
jest nieustawione (tzn. Pounset IFS
), to równoważneIFS
jest ustawienie$' \t\n'
(spacja, tabulator i nowa linia). JeśliIFS
jest ustawiona na pustą wartość (to właśnie oznacza tutaj „null”) (tj. PoIFS=
lubIFS=''
lubIFS=""
), w ogóle nie jest wykonywane dzielenie pól (i$*
, który zwykle używa pierwszego znaku$IFS
, używa znaku spacji).P3: Jeśli chcesz mieć domyślne
IFS
zachowanie, możesz użyćunset IFS
. Jeśli chceszIFS
jawnie ustawić tę wartość domyślną, możesz umieścić literalne spacje, tabulatory, znaki nowej linii w pojedynczych cudzysłowach. W ksh93, bash lub zsh możesz użyćIFS=$' \t\n'
. Przenośnie, jeśli chcesz uniknąć literowego znaku tabulacji w pliku źródłowym, możesz użyćP4: Przy
IFS
ustawieniu pustej wartościread -r line
ustawialine
całą linię oprócz końcowej nowej linii. Za pomocąIFS=" "
, spacje na początku i na końcu linii są przycinane. Przy domyślnej wartościIFS
tabulatory i spacje są przycinane.źródło
$@
istnieją pewne różnice między powłokami w kontekstach innych niż listy, takie jakIFS=; var=$@
). Należy zauważyć, że gdy IFS jest pusty, żadne dzielenie słów nie jest wykonywane, ale $ var nadal rozwija się do żadnego argumentu zamiast pustego argumentu, gdy $ var jest pusty, a globowanie nadal obowiązuje, więc nadal musisz cytować zmienne (nawet jeśli wyłączyć globbing)Pytanie 1 Podział pola.
Tak, oba wskazują na ten sam pomysł.
P2: Kiedy IFS ma wartość zerową ?
Tak, wszystkie trzy oznaczają to samo: Nie należy wykonywać podziału pól / słów. Wpływa to również na drukowanie pól (tak jak w przypadku
echo "$*"
), wszystkie pola zostaną połączone razem bez spacji.P3: (część a) Unset IFS.
Co jest dokładnie równoważne z:
Oznacza to, że „Podział pola” będzie dokładnie taki sam z domyślną wartością IFS, lub zostanie rozbrojony.
To wcale NIE oznacza, że IFS będzie działać tak samo we wszystkich warunkach. Mówiąc dokładniej, wykonanie
OldIFS=$IFS
ustawi zmienną varOldIFS
na null , a nie domyślną. A próba przywrócenia IFS w ten sposóbIFS=OldIFS
spowoduje ustawienie wartości zerowej na IFS, a nie pozostawienie go tak jak wcześniej. Uważaj !!.P3: (część b) Przywróć IFS.
W przypadku zsh, ksh i bash (AFAIK) IFS można ustawić na wartość domyślną jako:
Zrobione, nie musisz nic więcej czytać.
Ale jeśli musisz ponownie ustawić IFS dla sh, może się to skomplikować.
Spójrzmy od najłatwiejszego do wykonania bez żadnych wad (oprócz złożoności).
1. - Unset IFS.
Moglibyśmy
unset IFS
(Przeczytaj część 3 część A powyżej).2.- Zamień znaki.
Aby obejść ten problem, zamiana wartości tabulatorów i znaków nowej linii ułatwia ustawienie wartości IFS, a następnie działa w równoważny sposób.
Ustaw IFS na <space><newline> <tab> :
3.- Prosty? rozwiązanie:
Jeśli istnieją skrypty potomne, które wymagają poprawnego ustawienia IFS, zawsze możesz ręcznie napisać:
Gdzie sekwencja została wpisana ręcznie:,
IFS=
'spacetabnewline'sekwencja, która faktycznie została poprawnie wpisana powyżej (Jeśli musisz potwierdzić, edytuj tę odpowiedź). Ale kopiowanie / wklejanie z przeglądarki ulegnie uszkodzeniu, ponieważ przeglądarka wyciska / ukrywa białe znaki. Utrudnia to dzielenie się kodem, jak napisano powyżej.4.- Kompletne rozwiązanie.
Pisanie kodu, który można bezpiecznie skopiować, zwykle wymaga jednoznacznych znaków specjalnych do wydrukowania.
Potrzebujemy kodu, który „produkuje” oczekiwaną wartość. Ale nawet jeśli jest poprawny pod względem koncepcyjnym, ten kod NIE ustawi końcowego
\n
:Dzieje się tak, ponieważ pod większością powłok wszystkie końcowe znaki nowej linii
$(...)
lub`...`
podstawienia poleceń są usuwane podczas rozwijania.Musimy użyć trika dla sh:
Alternatywnym sposobem może być ustawienie IFS jako wartości środowiskowej z bash (na przykład), a następnie wywołanie sh (wersje, które akceptują ustawienie IFS przez środowisko), ponieważ:
Krótko mówiąc, sh sprawia, że resetowanie IFS do domyślnych jest dość dziwną przygodą.
P4: W rzeczywistym kodzie:
Po pierwsze: nie wiem, czy
echo $line
(z cytowanym zmiennym NOT) jest na porpouse, czy nie. Wprowadza drugi poziom „podziału pola”, którego odczytu nie ma. Więc odpowiem na oba. :)Za pomocą tego kodu (abyś mógł potwierdzić). Będziesz potrzebował przydatnego xxd :
Dostaję:
Pierwsza wartość to tylko poprawna wartość
IFS=
'spacetabnewline'Kolejny wiersz to wszystkie wartości szesnastkowe, które
$a
ma var , oraz nowy wiersz „0a” na końcu, gdy zostanie podany każdej komendzie odczytu.Następny wiersz, dla którego IFS ma wartość null, nie wykonuje „podziału pola”, ale nowa linia jest usuwana (zgodnie z oczekiwaniami).
Następne trzy wiersze, ponieważ IFS zawiera spację, usuwają początkowe spacje i ustawiają linię var na pozostałą saldo.
Ostatnie cztery wiersze pokazują, co zrobi niecytowana zmienna. Wartości zostaną podzielone na (kilka) spacji i zostaną wydrukowane jako:
bar,baz,qux,
źródło
unset IFS
czyści IFS, nawet jeśli później domniemywa się, że będzie to „\ t \ n”:Testowane na wersjach bash 4.2.45 i 3.2.25 z tym samym zachowaniem.
źródło
unset
odIFS
, jak wyjaśniono w komentarzach zaakceptowanej odpowiedzi tutaj.