Kiedy mogę użyć tymczasowego IFS do podziału pola?


19

W bash powiedz, że masz var=a.b.c., a następnie:

$ IFS=. printf "%s\n" $var
a.b.c

Jednak takie użycie IFSdziała podczas tworzenia tablicy:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Jest to bardzo wygodne, jasne, ale gdzie to jest udokumentowane? Szybki odczyt sekcji dotyczących tablic lub dzielenia wyrazów w dokumentacji Bash nie daje żadnej wskazówki. Poszukiwanie IFSprzez dokumentacji pojedynczej strony nie daje żadnych wskazówek na temat tego efektu albo.

Nie jestem pewien, kiedy mogę w niezawodny sposób:

IFS=x do something

I oczekuj, że IFSwpłynie to na podział pola.

Odpowiedzi:


28

Podstawową ideą jest to, że VAR=VALUE some-commandustawia VARsię VALUEna wykonanie some-commandkiedy some-commandjest zewnętrznym poleceniem i nie robi się to bardziej wymyślne. Jeśli połączysz tę intuicję z pewną wiedzą na temat działania powłoki, w większości przypadków powinieneś znaleźć właściwą odpowiedź. Odniesieniem do POSIX są „Proste polecenia” w rozdziale „Język poleceń powłoki” .

Jeśli some-commandjest poleceniem zewnętrznym , VAR=VALUE some-commandjest równoważne z env VAR=VALUE some-command. VARjest eksportowany w środowisku some-command, a jego wartość (lub brak wartości) w powłoce się nie zmienia.

Jeśli some-commandjest funkcją , to VAR=VALUE some-commandjest równoważne VAR=VALUE; some-command, tj . Przypisanie pozostaje na miejscu po powrocie funkcji, a zmienna nie jest eksportowana do środowiska. Powodem tego jest konstrukcja powłoki Bourne'a (a następnie zgodność wsteczna): nie miała możliwości zapisywania i przywracania wartości zmiennych wokół wykonywania funkcji. Brak eksportowania zmiennej ma sens, ponieważ funkcja jest wykonywana w samej powłoce. Jednak ksh (w tym zarówno ATT ksh93, jak i pdksh / mksh), bash i zsh implementują bardziej użyteczne zachowanie, które VARjest ustawiane tylko podczas wykonywania funkcji (jest również eksportowane). W ksh odbywa się to, jeśli funkcja jest zdefiniowana za pomocą składni kshfunction NAME …, nie jeśli jest zdefiniowany ze standardową składnią NAME (). W bash odbywa się to tylko w trybie bash, a nie w trybie POSIX (po uruchomieniu z POSIXLY_CORRECT=1). W zsh odbywa się to, jeśli posix_builtinsopcja nie jest ustawiona; ta opcja nie jest ustawiona domyślnie, ale jest włączona przez emulate shlub emulate ksh.

Jeśli some-commandjest wbudowane, zachowanie zależy od typu wbudowanego. Specjalne wbudowane zachowują się jak funkcje. Specjalne wbudowane to te, które muszą zostać zaimplementowane w powłoce, ponieważ wpływają na powłokę stanu (np. Wpływają na breakprzepływ sterowania, cdwpływają na bieżący katalog, setwpływają na parametry pozycyjne i opcje…). Inne wbudowane funkcje są wbudowane tylko dla wydajności i wygody (głównie - np. Funkcja bash printf -vmoże być zaimplementowana tylko przez wbudowane) i zachowują się jak polecenie zewnętrzne.

Przypisanie odbywa się po rozwinięciu aliasu, więc jeśli some-commandjest to alias , rozwiń go najpierw, aby znaleźć, co się stanie.

Zauważ, że we wszystkich przypadkach przypisanie jest wykonywane po przeanalizowaniu wiersza poleceń, w tym wszelkich podstawień zmiennych w samym wierszu poleceń. Tak var=a; var=b echo $vardrukuje a, ponieważ $varjest oceniane przed przypisaniem. I w ten sposób IFS=. printf "%s\n" $varużywa starej IFSwartości do podziału $var.

Omówiłem wszystkie typy poleceń, ale jest jeszcze jeden przypadek: gdy nie ma polecenia do wykonania , tj. Jeśli polecenie składa się tylko z przypisań (i ewentualnie przekierowań). W takim przypadku przypisanie pozostaje na miejscu . VAR=VALUE OTHERVAR=OTHERVALUEjest równoważne z VAR=VALUE; OTHERVAR=OTHERVALUE. Więc po IFS=. arr=($var), IFSpozostaje ustawiony na .. Ponieważ można użyć $IFSw przypisaniu arrz oczekiwaniem, że ma już swoją nową wartość, sensowne jest, aby nowa wartość parametru IFSbyła używana do rozszerzenia $var.

Podsumowując, możesz użyć tylko IFSdo tymczasowego podziału pola:

  • uruchamiając nową powłokę lub podpowłokę (np. third=$(IFS=.; set -f; set -- $var; echo "$3")jest to skomplikowany sposób, z third=${var#*.*.}wyjątkiem tego, że zachowują się inaczej, gdy wartość parametru varzawiera mniej niż dwa .znaki);
  • w ksh, IFS=. some-functiongdzie gdzie some-functionjest zdefiniowane za pomocą składni ksh function some-function …;
  • w bash i zsh, o IFS=. some-functionile działają one w trybie macierzystym, a nie w trybie zgodności.

IFSpozostaje ustawiony na .” Eek. Po przeczytaniu pierwszej części ma to sens, ale zanim opublikowałem to pytanie, nie spodziewałbym się tego.
muru

1
To jest odpowiedź na inne pytanie
schily

Kilka dodatkowych wyjaśnień w tej odpowiedzi sprzed kilku lat .
Ti Strga

6

Odpowiedź @Gilles jest naprawdę świetna, wyjaśnia (szczegółowo) złożony problem.

Uważam jednak, że odpowiedź na pytanie, dlaczego to polecenie:

$ IFS=. printf "%s\n" $var
a.b.c

działa tak, jak działa. Prostym pomysłem jest przeanalizowanie całego wiersza poleceń przed jego wykonaniem. I że każde „słowo” jest przetwarzane raz przez powłokę.
Te zadania, takie jak IFS=., są opóźnione (krok 4 jest ostatni):

4.- Każde przypisanie zmiennej należy rozszerzyć ...

do momentu tuż przed wykonaniem polecenia i przetworzeniem wszystkich rozszerzeń argumentów w celu zbudowania tej linii wykonywalnej:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

Wartość parametru $varjest rozszerzana o „stary” IFS do, a.b.czanim komenda printfotrzyma argumenty "%s\n"i a.b.c.

Eval

Jeden poziom opóźnienia można wprowadzić poprzez eval:

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

Linia jest analizowana (pierwszy raz) i „IFS =.” jest ustawiony na środowisko, ponieważ:

$ printf '%s\n' $var

Następnie jest ponownie analizowany:

$ printf '%s\n' a b c

I wykonano na to:

a
b
c

Wartość $var(ABC) jest dzielona z wartością IFS w użyciu: ..

Środowisko

Złożona i skomplikowana część jest ważna w środowisku, kiedy !!!

Wyjaśnia to bardzo dobrze pierwsza część odpowiedzi Gillesa.

Z dodatkowym szczegółem.

Po wykonaniu tego polecenia:

$ IFS=. arr=($var)

Wartość IFS jest zachowana w obecnym środowisku, tak:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS dla pojedynczej instrukcji.

Można jednak tego uniknąć: ustawienie IFS dla pojedynczej instrukcji

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 

2

Twoje pytanie dotyczące

var=a.b.c
IFS=. printf "%s\n" $var

jest skrzynką narożną.

Wynika to z faktu, że macro expansionpolecenie in występuje przed ustawieniem zmiennej powłoki IFS=..

Innymi słowy: po $varrozwinięciu poprzednia IFSwartość jest aktywna, a następnie IFSjest ustawiona na '.'.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.