Я создаю файл с разделенными табуляцией полями.
echo foo$'\t'bar$'\t'baz$'\n'foo$'\t'bar$'\t'baz > input
У меня есть следующий скрипт с именем zsh.sh
#!/usr/bin/env zsh
while read line; do
<<<$line cut -f 2
done < "$1"
Я проверяю это.
$ ./zsh.sh input
bar
bar
Это отлично работает. Однако, когда я изменяю первую строку, чтобы вызвать bash
вместо этого, это терпит неудачу.
$ ./bash.sh input
foo bar baz
foo bar baz
Почему это терпит неудачу bash
и работает с zsh
?
Дополнительное устранение неполадок
- Использование прямых путей в шебанге вместо того
env
же самого поведения. - Трубопровод с
echo
использованием здесь-строки<<<$line
также приводит к тому же самому поведению. то естьecho $line | cut -f 2
. - Использование
awk
вместоcut
работает для обеих оболочек. то есть<<<$line awk '{print $2}'
.
bash
zsh
quoting
whitespace
here-string
Sparhawk
источник
источник
echo -e 'foo\tbar\tbaz\n...'
,echo $'foo\tbar\tbaz\n...'
или ,printf 'foo\tbar\tbaz\n...\n'
или вариации этих. Это избавляет вас от необходимости индивидуально переносить каждую вкладку или новую строку.Ответы:
Что происходит, это
bash
заменяет вкладки пробелами. Вы можете избежать этой проблемы, говоря"$line"
вместо этого, или явно сокращая пробелы.источник
\t
и заменяет ее пробелом?<<< $line
, делит,bash
но не глобус. Нет никаких причин, по которым это могло бы быть разделено здесь, как<<<
ожидается, одно слово. В этом случае он разделяется и затем присоединяется, что не имеет большого смысла и противоречит всем другим реализациям оболочек, которые поддерживаются<<<
до или послеbash
. ИМО это ошибка.Это связано с тем
<<< $line
,bash
что разделение слов выполняется (хотя и не глобально), так$line
как оно там не заключено в кавычки, а затем соединяет результирующие слова с пробелом (и помещает их во временный файл, после которого следует символ новой строки и делает его стандартнымcut
).tab
случается в значении по умолчанию$IFS
:Решение с помощью
bash
кавычки переменной.Обратите внимание, что это единственная оболочка, которая делает это.
zsh
(откуда<<<
взято, вдохновлено портом Unixrc
)ksh93
,mksh
иyash
которые также поддерживают<<<
, не делают этого.Когда речь идет о массивах
mksh
,yash
иzsh
присоединиться на первый символ$IFS
,bash
иksh93
на пространстве.Существует разница между
zsh
/yash
иmksh
(по крайней мере, версия R52), когда$IFS
пусто:Поведение является более согласованным для всех оболочек, когда вы используете
"${a[*]}"
(за исключением того, чтоmksh
ошибка$IFS
остается пустой).Во-
echo $line | ...
первых, это обычный оператор split + glob во всех Bourne-подобных оболочках, ноzsh
(и обычные проблемы, связанные сecho
).источник
Проблема в том, что вы не цитируете
$line
. Чтобы исследовать, измените два сценария, чтобы они просто печатали$line
:и
Теперь сравните их вывод:
Как вы можете видеть, поскольку вы не цитируете
$line
, вкладки неправильно интерпретируются bash. Zsh, кажется, справляется с этим лучше. Теперь по умолчаниюcut
используется\t
в качестве разделителя полей. Следовательно, поскольку вашbash
сценарий потребляет вкладки (из-за оператора split + glob), онcut
видит только одно поле и действует соответственно. То, что вы действительно запускаете, это:Итак, чтобы ваш скрипт работал как положено в обеих оболочках, заключите вашу переменную в кавычки:
Затем оба выдают одинаковый результат:
источник
bash.sh
Как уже было сказано, более переносимый способ использования переменной - заключить ее в кавычки:
В bash есть разница в реализации:
Это результат большинства оболочек:
Только bash разделяет переменную справа от
<<<
кавычек.Однако это было исправлено в bash версии 4.4.
Это означает, что значение
$IFS
влияет на результат<<<
.С линией:
Все оболочки используют первый символ IFS для объединения значений.
При этом
"${l[@]}"
необходим пробел для разделения различных аргументов, но некоторые оболочки предпочитают использовать значение из IFS (это правильно?).При нулевом IFS значения должны быть объединены, как в этой строке:
Но и lksh, и mksh не могут этого сделать.
Если мы перейдем к списку аргументов:
И yash, и zsh не могут разделить аргументы. Это ошибка?
источник
zsh
/yash
и"${l[@]}"
в контексте, не относящемся к списку, это дизайн, который"${l[@]}"
является особенным только в контексте списка. В контекстах, не относящихся к списку, разделение невозможно, вам нужно как-то объединить элементы. Присоединение к первому символу $ IFS более согласованно, чем соединение с пробелом IMO.dash
делает это также (dash -c 'IFS=; a=$@; echo "$a"' x a b
). Тем не менее, POSIX намеревается изменить этот IIRC. Смотрите это (длинное) обсуждениеvar=$@
неопределенного.