Коли я можу використовувати тимчасовий IFS для поділу поля?


19

Скажімо, в басі, скажіть, що у вас є var=a.b.c.:

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

Однак таке використання IFSдійсно діє під час створення масиву:

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

Це дуже зручно, звичайно, але де це документально підтверджено? Швидке читання розділів про масиви або розділення слів у документації на Bash не дає жодних ознак. Пошуки IFSчерез документацію однієї сторінки не дають ніяких натяків по приводу цього ефекту або.

Я не впевнений, коли можу надійно зробити:

IFS=x do something

І очікуйте, що IFSце вплине на розбиття поля.

Відповіді:


28

Основна ідея полягає в тому, що VAR=VALUE some-commandнабори VARдля VALUEдля виконання , some-commandколи some-commandє зовнішня команда, і вона не отримує більше фантазії , ніж це. Якщо ви поєднуєте цю інтуїцію з деякими знаннями про те, як працює оболонка, у більшості випадків вам слід підійти з правильною відповіддю. Посилання POSIX - "Прості команди" у розділі "Мова командної оболонки" .

Якщо some-commandце зовнішня команда , VAR=VALUE some-commandеквівалентна env VAR=VALUE some-command. VARекспортується в середовище some-command, і його значення (або відсутність значення) в оболонці не змінюється.

Якщо some-commandце функція , то VAR=VALUE some-commandвона еквівалентна VAR=VALUE; some-command, тобто присвоєння залишається на місці після повернення функції, а змінна не експортується в середовище. Причина цього пов'язана з дизайном оболонки Борна (а згодом і з зворотною сумісністю): він не мав можливості зберігати та відновлювати змінні значення навколо виконання функції. Не експортувати змінну має сенс, оскільки функція виконується в самій оболонці. Однак, ksh (включаючи ATT ksh93 та pdksh / mksh), bash та zsh реалізують більш корисну поведінку, де VARвстановлено лише під час виконання функції (вона також експортується). У ksh це робиться, якщо функція визначена синтаксисом kshfunction NAME …, не якщо це визначено зі стандартним синтаксисом NAME (). У bash це робиться тільки в режимі bash, а не в режимі POSIX (при запуску з POSIXLY_CORRECT=1). У zsh це робиться, якщо posix_builtinsпараметр не встановлений; ця опція не встановлена ​​за замовчуванням, але увімкнена emulate shабо emulate ksh.

Якщо some-commandвбудований, поведінка залежить від типу вбудованого. Спеціальні вбудовані поводяться як функції. Спеціальні вбудовані модулі - це ті, які потрібно реалізувати всередині оболонки, оскільки вони впливають на стан оболонки (наприклад, breakвпливають на керуючий потік, cdвпливають на поточний каталог, setвпливають на позиційні параметри та параметри ...). Інші вбудовані вбудовані лише для продуктивності та зручності (в основному - наприклад, функцію bash printf -vможе реалізовувати лише вбудований), і вони поводяться як зовнішня команда.

Присвоєння відбувається після розширення псевдоніма, тому, якщо some-commandце псевдонім , спершу розгорніть його, щоб знайти те, що відбувається.

Зауважте, що у всіх випадках призначення виконується після розбору командного рядка, включаючи будь-яку заміну змінної в самому командному рядку. Отже, var=a; var=b echo $varдрукується a, тому що $varоцінюється до того, як відбудеться завдання. І таким чином IFS=. printf "%s\n" $varвикористовує старе IFSзначення для розділення $var.

Я охоплював усі типи команд, але є ще один випадок: коли немає команди для виконання , тобто якщо команда складається лише з призначень (і, можливо, перенаправлень). У цьому випадку завдання залишається на місці . VAR=VALUE OTHERVAR=OTHERVALUEеквівалентно VAR=VALUE; OTHERVAR=OTHERVALUE. Отже після IFS=. arr=($var), IFSзалишається налаштованим .. Оскільки ви можете використовувати $IFSв призначенні до arrтого, що він вже має нове значення, має сенс, що нове значення IFSвикористовується для розширення $var.

Підсумовуючи, ви можете використовувати лише IFSдля тимчасового поділу поля:

  • запустивши нову оболонку або підшару (наприклад third=$(IFS=.; set -f; set -- $var; echo "$3"), це складний спосіб виконання, third=${var#*.*.}за винятком того, що вони ведуть себе по-різному, коли значення varмістить менше двох .символів);
  • в ksh, IFS=. some-functionде some-functionвизначено синтаксисом ksh function some-function …;
  • в bash і zsh, IFS=. some-functionякщо вони працюють в рідному режимі на відміну від режиму сумісності.

" IFSзалишається встановленим ." Eek. Прочитавши першу частину, це має сенс, але перед тим, як я опублікував цю Q, я б цього не очікував.
муру

1
Це відповідь на інше запитання
schily

Деякі додаткові пояснення у цій відповіді від декількох років тому .
Ti Strga

6

Відповідь @Gilles дійсно чудова, він пояснює (докладно) складне питання.

Однак я вважаю, що відповідь, чому ця команда:

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

працює так, як це робиться, це проста ідея, що весь командний рядок розбирається перед його виконанням. І що кожне «слово» обробляється один раз оболонкою.
Ці завдання, як IFS=., затримуються (крок 4 є останнім):

4.- Кожне призначення змінної має бути розширене ...

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

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

Значення $varрозширюється на "старий" IFS до a.b.cтого, як команді printfбуде надано аргументи "%s\n"і a.b.c.

Евал

Один рівень затримки може бути введений eval:

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

Рядок аналізується (1-й раз) та "IFS =". встановлюється для навколишнього середовища таким чином:

$ printf '%s\n' $var

Потім він знову розбирається на це:

$ printf '%s\n' a b c

І виконано до цього:

a
b
c

Значення $var(ABC) ділиться зі значенням КСФ у використанні: ..

Середовище

Складна і хитра частина - це те, що діє в навколишньому середовищі, коли !!!

Це дуже добре пояснено у першій частині відповіді Жиля.

З додатковою деталлю.

Коли ця команда виконується:

$ IFS=. arr=($var)

Значення IFS зберігається в сучасних умовах, так:

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

IFS для однієї заяви.

Але цього можна уникнути: Встановлення IFS для одного твердження

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

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

2

Ваше питання щодо

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

- кутовий корпус.

Це відбувається тому, що macro expansionв команді відбувається до встановлення змінної оболонки IFS=..

Іншими словами: коли $varрозгортається, попереднє IFSзначення активне, тоді IFSвстановлюється значення '.'.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.