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=" "
-----   --  --   --------       -----      ---------   -------                        

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 Bsą niezmienione .

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).


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.


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,
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.