Ответ 1
Да, это стандартное поведение (см. Спецификацию read
и разбиение поля). Несколько оболочек (ash
-based, включая dash
, pdksh
-based, zsh
, yash
по крайней мере) использовали не для этого, но кроме zsh
(если не в режиме POSIX), busybox
sh, большинство из них были обновлены для Соответствие POSIX.
То же самое для:
$ var='a:b:c:' IFS=:
$ set -- $var; echo "$#"
3
(см. то, как спецификация POSIX для read
фактически отменяет механизм разделения полей, где a:b:c:
разделяется на 3 поля, и поэтому с IFS=: read -r abc
, столько полей, сколько переменных).
Обоснованием является то, что в ksh
(на котором основана спецификация POSIX) $IFS
(первоначально в оболочке Bourne, внутренний разделитель полей) стал полевым разделителем, я думаю, что любой список элементов (не содержащий разделителя) может быть представлен.
Когда $IFS
является разделителем, нельзя представить список одного пустого элемента (""
разбивается на список из 0 элементов, ":"
в список из двух пустых элементов¹). Когда это разделитель, вы можете выразить список нулевого элемента с помощью ""
или один пустой элемент с ":"
или двумя пустыми элементами с "::"
.
Это немного неудачно, поскольку одним из наиболее распространенных способов использования $IFS
является разделение $PATH
. A $PATH
like /bin: /usr/bin:
предназначен для разделения на "/bin"
, "/usr/bin"
, ""
, а не только "/bin"
и "/usr/bin"
.
Теперь, с оболочками POSIX (но не все оболочки совместимы в этом отношении), для разделения слов при расширении параметра, с которым можно работать:
IFS=:; set -o noglob
for dir in $PATH""; do
something with "${dir:-.}"
done
Этот трейлинг ""
гарантирует, что если $PATH
заканчивается в трейлинг :
добавляется дополнительный пустой элемент. А также, что пустой $PATH
рассматривается как один пустой элемент, как и должно быть.
Однако этот подход не может быть использован для read
.
За исключением переключения на zsh
, нет никакой простой работы, кроме вставки дополнительной :
и удалите ее потом, как:
echo a:b:c: | sed 's/:/::/2' | { IFS=: read -r x y z; z=${z#:}; echo "$z"; }
Или (менее портативный):
echo a:b:c: | paste -d: - /dev/null | { IFS=: read -r x y z; z=${z%:}; echo "$z"; }
Я также добавил -r
который вы обычно хотите использовать при read
.
Скорее всего, здесь вы захотите использовать надлежащую утилиту для обработки текста, такую как sed
/awk
/perl
вместо того, чтобы писать свернутый и, вероятно, неэффективный код вокруг read
который не был разработан для этого.
¹ Хотя в оболочке Bourne все еще было разделено на нулевые элементы, поскольку не было различий между IFS-пробелами и символами IFS-non-whitespace, что также было добавлено ksh