W `while IFS = read..`, dlaczego IFS nie ma wpływu?

12

Mogę mieć coś absolutnie nie tak, ale wydaje mi się to przekonujące, że ustawienie IFS jako jednego z poleceń na liście zadań do wykonania / wykonania nie ma absolutnie żadnego efektu.
Zewnętrzny IFS (poza whilekonstrukcją) dominuje we wszystkich przykładach pokazanych w skrypcie poniżej.

Co tu się dzieje? Czy mam błędne wyobrażenie o tym, co IFS robi w tej sytuacji? Spodziewałem się, że wyniki podziału tablicy będą takie, jak pokazano w kolumnie „oczekiwany”.


#!/bin/bash
xifs() { echo -n "$(echo -n "$IFS" | xxd -p)"; } # allow for null $IFS 
show() { x=($1) 
         echo -ne "  (${#x[@]})\t |"
         for ((j=0;j<${#x[@]};j++)); do 
           echo -n "${x[j]}|"
         done
         echo -ne "\t"
         xifs "$IFS"; echo
}
data="a  b   c"
echo -e "-----   --  -- \t --------\tactual"
echo -e "outside        \t  IFS    \tinside" 
echo -e "loop           \t Field   \tloop" 
echo -e "IFS     NR  NF \t Split   \tIFS (actual)" 
echo -e "-----   --  -- \t --------\t-----"
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 1'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 2'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 3'; show "$REPLY"; done
IFS=" ";      xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 4'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 5'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 6'; show "$REPLY"; done
IFS=;         xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 7'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t 8'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 9'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t10'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t11'; show "$REPLY"; done
echo -e "-----   --  -- \t --------\t-----"

Wynik:

-----   --  --   --------       actual   
outside           IFS           inside                assigned   
loop             Field          loop    #              inner
IFS     NR  NF   Split          IFS     #  expected    IFS
-----   --  --   --------       -----   #  ---------  --------
20090a   1  (3)  |a|b|c|        20090a  #                              
20090a   2  (3)  |a|b|c|        20090a  #  |a  b   c|  IFS=
20090a   3  (3)  |a|b|c|        20090a  #  |a  |   c|  IFS=b
20       4  (3)  |a|b|c|        20      #                          
20       5  (3)  |a|b|c|        20      #  |a  b   c   IFS=
20       6  (3)  |a|b|c|        20      #  |a  |   c|  IFS=b
         7  (1)  |a  b   c|             #                          
         8  (1)  |a  b   c|             #  |a|b|c|     IFS=" "
         9  (1)  |a  b   c|             #  |a  |   c|  IFS=b
62      10  (2)  |a  |   c|     62      #  |a  b   c|  IFS=
62      11  (2)  |a  |   c|     62      #  |a|b|c|     IFS=" "
-----   --  --   --------       -----      ---------   -------                        
Peter.O
źródło

Odpowiedzi:

17

(Przepraszam, długie wyjaśnienie)

Tak, IFSzmienna w while IFS=" " read; do …nie ma wpływu na resztę kodu.

Najpierw sprecyzujmy, że wiersz poleceń powłoki zawiera dwa różne rodzaje zmiennych:

  • zmienne powłoki (które istnieją tylko w powłoce i są lokalne dla powłoki)
  • zmienne środowiskowe, które istnieją dla każdego procesu. Zazwyczaj są one zachowywane fork()i exec()dlatego procesy potomne je dziedziczą.

Po wywołaniu polecenia za pomocą:

  A=foo B=bar command

polecenie jest wykonywane w środowisku, w którym zmienna (środowiskowa) Ajest ustawiona na fooi Bjest ustawiona na bar. Ale w tym wierszu poleceń bieżące zmienne powłoki Ai Bniezmienione .

Różni się to od:

A=foo; B=bar; command

Tutaj powłoki zmienne Ai Bsą zdefiniowane i polecenie jest uruchamiane bez zmiennych środowiskowych Ai Bzdefiniowane. Wartości Ai Bsą niedostępne command.

Jednak jeśli niektóre zmienne powłoki są export-ed, odpowiednie zmienne środowiskowe są synchronizowane z odpowiednimi zmiennymi powłoki. Przykład:

export A
export B
A=foo; B=bar; command

Za pomocą tego kodu zarówno zmienne powłoki , jak i zmienne środowiskowe powłoki są ustawione na fooi bar. Ponieważ zmienne środowiskowe są dziedziczone przez podprocesy, commandbędą mogły uzyskać dostęp do ich wartości.

Aby wrócić do pierwotnego pytania, w:

IFS='a' read

readdotyczy to tylko . W rzeczywistości w tym przypadku readnie zależy na wartości IFSzmiennej. Używa się go IFStylko wtedy, gdy poprosisz o podzielenie wiersza (i zapisanie go w kilku zmiennych), np .:

echo "a :  b :    c" | IFS=":" read i j k; \
    printf "i is '%s', j is '%s', k is '%s'" "$i" "$j" "$k"

IFSnie jest używane przez, readchyba że jest wywoływane z argumentami. ( Edycja: Nie jest to do końca prawdą: białe znaki, tj. Spacje i tabulatory, obecne w IFSsą zawsze ignorowane na początku / końcu linii wprowadzania).

Stéphane Gimenez
źródło
Cóż za świetne wytłumaczenie! To takie proste! Przez kilka miesięcy byłem oszołomiony tą składnią „bez średnika” ; i jest to po prostu przypadek, co oznacza zmienną lokalną! .. rozcietrzewiacz otworzył mi ścieżkę (wielki czas) w innym pytaniu ... a ty właśnie nałożyłeś lukier na ciasto ... Byłem całą noc na ten temat, a na pewno warto było uzyskać tak dobre i jasne odpowiedzi! .. Dziękuję ..
Peter.O
Uhm. Musiałem przeczytać ten komentarz do edycji kilka razy, zanim go dostałem - masz na myśli, że obecne w nim białe znaki $IFSsą usuwane na początku / na końcu linii wprowadzania, tak przypuszczam? (Jak to działa.)
zrajm
Wartość IFS jest ważna nawet podczas odczytywania jednej zmiennej, ponieważ powłoka nadal dzieli słowa na danych wejściowych. Tak na przykład, wpisując znaki a<tab>bw read varspowoduje var o wartości a<space>b, ale jeśli zamiast masz IFS='<newline>' read varwtedy wartość var będzie a<tab>b.
John Hascall,
8

Mówiąc prościej, musisz czytać więcej niż jedną zmienną na raz, aby IFS=<something> read ...konstrukcja miała widoczny efekt w twoich przykładach 1 .

Brakuje zakresu readw przykładach. W testach nie ma modyfikacji IFS w pętli. Pozwólcie, że wskażę dokładnie, gdzie działa drugi IFS w każdej z twoich linii:

 IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo ...
                                                      ^      ^
                                                      |      |
                                          from here --'       `- to here :)

Jest tak jak w przypadku każdego programu wykonanego w powłoce. Zmienna zdefiniowana (ponownie) w wierszu poleceń wpływa na wykonanie programu. I tylko to (ponieważ nie eksportujesz). Dlatego, aby skorzystać z przedefiniowania IFSw takim wierszu, musisz poprosić reado przypisanie wartości do więcej niż jednej zmiennej . Spójrz na te przykłady:

 $ data="a  b   c"
 $ echo "$data" | while           read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a|b||c|
 $ echo "$data" | while IFS=      read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a b c||||
 $ echo "$data" | while IFS='a'   read A B C; do echo \|$A\|$B\|\|$C\|; done
 || b c|||
 $ echo "$data" | while IFS='ab'  read A B C; do echo \|$A\|$B\|\|$C\|; done
 || || c|

1 Jak właśnie dowiedziałem się od Gillesa , może być korzystne ustawienie IFS=''(puste) podczas odczytu tylko jednego pola: pozwala to uniknąć obcięcia białych znaków na początku linii.

rozcietrzewiacz
źródło
Dobrze .. Dzięki ... Mam go tym razem .. i uwielbiam twój szkic :)
Peter.O
OK, teraz przeczytałem twój komentarz, który pokazuje, że nie zauważyłeś mojej odpowiedzi na ten problem w drugim pytaniu. Może mógłbyś po prostu cofnąć drugi i usunąć to, ponieważ tak naprawdę jest to jeden ogólny problem?
rozcietrzewiacz
Tak, te dwa pytania mają pokrewny temat, ale tytuł drugiego brzmi „Dlaczego IFS= readpreferowane jest po prostu ponowne ustawienie zmiennej środowiskowej IFS”. Nie miałem wtedy świadomości, że zmienne lokalne mogą być ustawiane przez osobę wywołującą polecenie. To była odpowiedź na to pytanie. Z czasem rozwinęło się to, odnosząc się do głównej kwestii tego pytania, ale zanim zdałem sobie z tego sprawę, zadałem już to pytanie ... Być może dwa pytania są podobne do dwóch sedpytań, więc czuję, że utrzymam to asis ... Więcej tytułów dla pracowników Google do Google.
Peter.O,