У той час, коли IFS = read..`, чому IFS не робить ефекту?


12

У мене може бути щось абсолютно неправильне, але мені здається переконливим, що встановлення IFS як однієї з команд у списку попередньо зроблено / зроблено абсолютно не впливає.
Зовнішній IFS (поза whileконструкцією) переважає у всіх прикладах, показаних у сценарії нижче.

Що тут відбувається? У мене неправильне уявлення про те, що робить IFS у цій ситуації? Я очікував, що результати розбиття масиву будуть такими, як показано у стовпці "очікуваний".


#!/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-----"

Вихід:

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

Відповіді:


17

(Вибачте, довге пояснення)

Так, IFSзмінна in while IFS=" " read; do …не впливає на решту коду.

Давайте спочатку уточнимо, що командний рядок оболонки містить два різні види змінних:

  • змінні оболонки (які існують лише в оболонці і є локальними для оболонки)
  • змінні середовища, які існують для кожного процесу. Вони, як правило, зберігаються fork()і exec(), таким чином, дитячі процеси успадковують їх.

Коли ви викликаєте команду за допомогою:

  A=foo B=bar command

команда виконується в середовищі, де змінна (середовище) Aвстановлена fooі Bвстановлена bar. Але в цьому командному рядку поточні змінні оболонки Aі Bзалишаються незмінними .

Це відрізняється від:

A=foo; B=bar; command

Тут змінні оболонки Aі Bвизначаються, а команда виконується без змінних середовищ Aі не Bвизначається. Цінності Aта з Bних недоступні command.

Однак якщо деякі змінні оболонки є export-ed, відповідні змінні середовища синхронізуються з відповідними змінними оболонки. Приклад:

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

За допомогою цього коду і змінні оболонки, і змінні середовища оболонки встановлюються на fooта bar. Оскільки змінні середовища успадковуються підпроцесами, commandвони матимуть доступ до їх значень.

Щоб повернутися до початкового запитання, виконайте наведені нижче дії.

IFS='a' read

тільки readвпливає. А насправді в цьому випадку readбайдуже значення IFSзмінної. Він використовується IFSлише тоді, коли ви вимагаєте розбивати рядок (і зберігати в декількох змінних), наприклад у:

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

IFSне використовується, readякщо воно не викликається аргументами. ( Редагувати: Це не зовсім так: символи пробілу, тобто пробіл та вкладка, присутні у IFS, завжди ігноруються на початку / в кінці рядка введення.)


Яке чудове пояснення! Це так просто! Мене місяцями переслідував той синтаксис "без напівколонки" ; і це просто випадок, коли це означає локальну змінну! .. rozcietrzewiacz відкрив для мене шлях (великий час) в іншому питанні ... і ви щойно поставили глазур на торт ... Я був цілу ніч на цьому, і це, безумовно, вартувало таких гарних і чітких відповідей! .. Дякую ..
Пітер.О

Ум. Мені довелося прочитати цей коментар до редагування кілька разів, перш ніж я його отримав - ти маєш на увазі сказати, що символи пробілів, які присутні в $IFS, видаляються на початку / в кінці рядка введення, я вважаю? (
Ось

Погляньте на це: unix.stackexchange.com/questions/382963/…

Значення IFS є важливим навіть при читанні однієї змінної, оскільки оболонка все ще робить розбиття слів на вході. Так, наприклад, введення символів a<tab>bу read varрезультат призведе до значення var a<space>b, але якщо замість цього у вас є, IFS='<newline>' read varто значення var буде a<tab>b.
Джон Хаскалл

8

Простіше кажучи, ви повинні читати до декількох змінних одночасно, щоб IFS=<something> read ...конструкція мала видимий ефект у ваших прикладах 1 .

Ви пропускаєте сферу застосування readв прикладах. Там немає ні модифікації МФСА всередині циклу в тестових даних . Дозвольте мені точно вказати, де впливає другий IFS у кожному з ваших рядків:

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

Це як і будь-яка програма, виконана в оболонці. Змінна, яку ви (повторно) визначаєте в командному рядку, впливає на виконання програми. І тільки це (оскільки ви не експортуєте). Тому, щоб використовувати переосмислений IFSу такому рядку, вам доведеться попросити readпризначити значення більш ніж одній змінній . Подивіться наступні приклади:

 $ 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 Як я щойно дізнався від Жилла , може бути користь налаштування IFS=''(порожнього), читаючи лише одне поле: це дозволяє уникнути урізання пробілів на початку рядка.


Добре .. Дякую ... я цього разу отримав .. і я люблю твій ескіз :)
Peter.O

Гаразд, зараз я прочитав ваш коментар, який бачить, що ви не помітили моєї відповіді на це питання в іншому запитанні. Можливо, ви можете просто повернути інший і видалити це, оскільки це справді одна загальна проблема?
rozcietrzewiacz

Так, два питання мають суміжну тему, але назва другого - "Чому IFS= readвикористовується в перевазі просто перевстановлення змінної середовища IFS". Тоді я не знав, що локальні змінні можуть бути встановлені абонентом команди. То була відповідь на це питання. Це розвивалося далі, щоб вирішити головне питання цього питання, але до того моменту, як я зрозумів це, я вже задавав це питання ... Можливо, ці два питання схожі на два sedпитання, тому я відчуваю, що це не може бути ... Більше назв для googlers до Google.
Пітер.O
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.