Q1. Розщеплення поля.
Чи розділення поля те саме, що розділення слів?
Так, обидва вказують на одну і ту ж ідею.
Чи встановлено так IFS=''
само, як null, те саме, що і порожній рядок?
Так, усі три означають те саме: Розбиття на поле / слово не повинно здійснюватися. Також це впливає на поля друку (як і у випадку echo "$*"
), всі поля будуть об'єднані разом без місця.
Q3: (частина a) Не встановлено IFS.
У специфікації POSIX я прочитав наступне :
Якщо IFS не встановлено, оболонка повинна вести себе так, як якщо б значення IFS було <space><tab> <newline> .
Що точно рівнозначно:
За допомогою unset IFS
оболонки оболонка повинна поводитись так, як якщо IFS за замовчуванням.
Це означає, що "Розбиття поля" буде точно таким же зі значенням IFS за замовчуванням або не встановлено.
Це НЕ означає, що IFS працюватиме однаково в будь-яких умовах. Будучи більш конкретним, виконуючи OldIFS=$IFS
задасть вар OldIFS
до нуля , а не за замовчуванням. І намагаючись повернути IFS назад, як це, IFS=OldIFS
встановить IFS нульовим, а не збереже його невстановленим, як це було раніше. Стережись !!.
Q3: (частина б) Відновлення IFS.
Як я можу відновити значення IFS до типового. Скажіть, я хочу відновити значення за замовчуванням IFS. Як це зробити? (точніше, як я посилаюся на <tab> та <newline> ?)
Для zsh, ksh та bash (AFAIK) IFS може бути встановлено за замовчуванням як:
IFS=$' \t\n' # works with zsh, ksh, bash.
Зроблено, вам більше нічого не потрібно читати.
Але якщо вам потрібно перевстановити IFS для sh, це може стати складним.
Давайте розберемося з найпростішого до завершення без недоліків (крім складності).
1.- Скиньте IFS.
Ми могли просто unset IFS
(Прочитайте Q3, частину a, вище).
2.- Поміняйте символи.
Як вирішення, заміна значень табуляції та нового рядка спрощує встановлення значення IFS, а потім воно працює аналогічно.
Встановіть для IFS <space><newline> <tab> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.— Простий? рішення:
Якщо є дочірні сценарії, для яких потрібно правильно встановити IFS, ви завжди можете вручну записати:
IFS = '
'
Там, де була введена вручну послідовність: IFS=
'spacetabnewline'послідовність, яка фактично була правильно набрана вище (Якщо вам потрібно підтвердити, відредагуйте цю відповідь). Але копія / вставка з вашого браузера порушиться, оскільки браузер видавить / приховає пробіл. Складно ділити код, як написано вище.
4.- Повне рішення.
Для написання коду, який можна безпечно скопіювати, зазвичай передбачено однозначні вказівки для друку.
Нам потрібен якийсь код, який "виробляє" очікуване значення. Але, навіть якщо концептуально правильний, цей код НЕ встановлює проміжку \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Це трапляється тому, що в більшості оболонок всі розширення нових рядків $(...)
або `...`
підстановок команд видаляються при розширенні.
Нам потрібно використовувати трюк для sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Альтернативним способом може бути встановлення IFS як значення середовища з bash (наприклад), а потім виклик sh (його версії, які приймають IFS для встановлення через оточення), таким чином:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Коротше кажучи, sh робить скидання IFS за замовчуванням досить дивною пригодою.
Q4: фактичний код:
Нарешті, як би цей код:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
поводитися, якщо ми змінимо перший рядок на
while read -r line # Use the default IFS value
або до:
while IFS=' ' read -r line
По-перше: я не знаю, є echo $line
(з вар НЕ цитується) там на porpouse, чи ні. Він запроваджує другий рівень "розбиття поля", який читати не має. Тож я відповім обом. :)
За допомогою цього коду (щоб ви могли підтвердити). Вам знадобиться корисний xxd :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Я отримав:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Перше значення - це лише правильне значення IFS=
'spacetabnewline'
Наступний рядок - це всі шістнадцяткові значення, які $a
має var , та новий рядок '0a' в кінці, оскільки він буде наданий кожній команді читання.
Наступний рядок, для якого IFS недійсний, не виконує жодного "розбиття поля", але новий рядок видаляється (як очікувалося).
Наступні три рядки, оскільки IFS містить пробіл, видаліть початкові пробіли та встановіть рядок var до залишку залишку.
Останні чотири рядки показують, що буде робити без котирування змінна. Значення будуть розділені на (кілька) пробілів і будуть надруковані у вигляді:bar,baz,qux,
IFS
і невстановленийIFS
дуже різні. Відповідь на Q4 частково неправильна: внутрішні роздільники тут не торкаються, лише провідні та зворотні.