Pytanie 1 Podział pola.
Czy podział pola jest taki sam jak podział słów?
Tak, oba wskazują na ten sam pomysł.
Czy ustawienie jest IFS=''
takie samo jak null, tak samo jak pusty ciąg?
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.
W specyfikacji POSIX czytam :
Jeśli IFS nie jest ustawiony, powłoka zachowuje się tak, jakby wartość IFS to <space><tab> <newline> .
Co jest dokładnie równoważne z:
W przypadku znaku unset IFS
powłoka powinna zachowywać się tak, jakby IFS był domyślny.
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ą var OldIFS
na null , a nie domyślną. A próba przywrócenia IFS w ten sposób IFS=OldIFS
spowoduje ustawienie wartości zerowej na IFS, a nie pozostawienie go tak jak wcześniej. Uważaj !!.
P3: (część b) Przywróć IFS.
Jak mogę przywrócić domyślną wartość IFS? Powiedz, że chcę przywrócić domyślną wartość IFS. W jaki sposób mogę to zrobić? (dokładniej, jak mam odwoływać się do <tab> i <newline> ?)
W przypadku zsh, ksh i bash (AFAIK) IFS można ustawić na wartość domyślną jako:
IFS=$' \t\n' # works with zsh, ksh, bash.
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> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Prosty? rozwiązanie:
Jeśli istnieją skrypty potomne, które wymagają poprawnego ustawienia IFS, zawsze możesz ręcznie napisać:
IFS = „
„
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
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
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:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
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ż:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Krótko mówiąc, sh sprawia, że resetowanie IFS do domyślnych jest dość dziwną przygodą.
P4: W rzeczywistym kodzie:
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
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 :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Dostaję:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
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,
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.