У тих дивовижних частих випадках, коли вам потрібно щось обробити непорожні рядки всередині змінної (включаючи їх підрахунок), ви можете встановити IFS просто на новий рядок, а потім використати механізм розбиття слів оболонки, щоб зламати не порожні рядки.
Наприклад, ось невелика функція оболонки, яка підсумовує не порожні рядки у всіх наданих аргументах:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
Тут формуються дужки, а не дужки, для формування складної команди для функції функції. Це змушує функцію виконувати в підрозділі, щоб вона не забруднювала зовнішню світову змінну IFS та параметр розширення імені шляху при кожному виклику.
Якщо ви хочете переглядати непорожні рядки, ви можете зробити це аналогічно:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
Таким чином, маніпуляція IFS - це часто недооцінена техніка, яка також зручна для того, щоб робити такі речі, як аналіз імен шляхів, які можуть містити пробіли від стовпчикових вхідних даних, обмежених вкладками. Однак вам потрібно пам’ятати, що навмисне видалення символу пробілу, як правило, встановленого за замовчуванням в IFS налаштування пробілу-табуляції-нового рядка, може призвести до відключення розбиття слів у місцях, де ви, як правило, очікуєте його бачити.
Наприклад, якщо ви використовуєте змінні для складання складного командного рядка для чогось подібного ffmpeg
, ви можете включити -vf scale=$scale
лише тоді, коли змінна scale
встановлена на щось не порожнє. Зазвичай ви можете досягти цього за допомогою, ${scale:+-vf scale=$scale}
але якщо IFS не включає його звичайний пробільний символ під час розширення цього параметра, пробіл між -vf
і scale=
не буде використовуватися як роздільник слів і ffmpeg
передаватиметься -vf scale=$scale
як один аргумент, який він не зрозуміє.
Щоб виправити це, ви або повинні переконатися , що IFS був встановлений більш зазвичай , перш ніж робити ${scale}
розширення, або зробити два розкладання: ${scale:+-vf} ${scale:+scale=$scale}
. Розбиття слова, яке оболонка виконує в процесі початкового розбору командних рядків, на відміну від розщеплення, яке відбувається під час фази розширення обробки цих командних рядків, не залежить від IFS.
Щось інше, що може вартий вашого часу, якщо ви збираєтеся робити подібне, це створити дві глобальні змінні оболонки, щоб містити лише вкладку та новий рядок:
t=' '
n='
'
Таким чином, ви можете просто включати $t
і $n
в розширення, де вам потрібні вкладки та нові рядки, а не засмічувати весь код котируваним пробілом. Якщо ви хочете взагалі уникати процитованих пробілів у оболонці POSIX, яка не має іншого механізму для цього, вам printf
може допомогти, хоча вам потрібно трохи хитрувати, щоб вирішити проблему з видаленням останніх рядків у розширеннях команд:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
Іноді встановлення IFS, як якщо б це була змінна середовища для команди, добре працює. Наприклад, ось цикл, який зчитує ім'я шляху, який може містити пробіли та коефіцієнт масштабування з кожного рядка вхідного файлу з обмеженими вкладками:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
У цьому випадку read
вбудований бачить IFS встановленим лише на вкладку, тому він не розділяє рядок введення, яку він читає, і на пробіли. Але IFS=$t set -- $lines
це не працює: оболонка розширюється $lines
під час побудови set
аргументів вбудованого перед виконанням команди, тому тимчасова установка IFS таким чином, який застосовується лише під час виконання самого вбудованого, надто пізно. Ось чому фрагменти коду, які я дав, перш за все, встановив IFS в окремий крок, і чому їм доводиться вирішувати питання щодо його збереження.
wc -l
точно еквівалентний оригіналу:<<<$foo
додає новий рядок до значення$foo
(навіть якщо він$foo
був порожнім). У своїй відповіді я пояснюю, чому це, можливо, не було того, що хотілося, але це те, що його запитали.