Несогласованность во многом объясняется историческими причинами.
Использование квадратных скобок в качестве условной команды произошло после использования скобок в шаблонах подстановочных знаков. Итак, в то время, когда [-n foo]
появился на сцене, [foo]
уже означало «файл с именем f
или o
». Хотя небольшое использование оператора скобок на практике противоречило бы реалистичному использованию скобок в подстановочных знаках, авторы предпочли не рисковать нарушением существующих скриптов путем изменения синтаксиса. Этот выбор дизайна также упростил реализацию: изначально [
было реализовано как внешняя команда, чего нельзя было бы сделать, если бы пробел после [
был необязательным. (Современные системы по-прежнему имеют [
в качестве внешней команды, но почти все современные оболочки также имеют встроенную команду.)
По тем же причинам ваш «рабочий» код на самом деле неверен в большинстве случаев. $ 1
не означает «значение параметра 1»: это означает «взять значение параметра 1, разделить его на отдельные слова через пробел (или любое другое значение IFS
] , и интерпретировать каждое слово как узор шара ».Чтобы написать «значение параметра 1», нужны двойные кавычки: «$ 1»
. См. Когда необходимо использовать двойные кавычки? для более подробной информации. Вам не нужно этого понимать; все, что вам нужно запомнить: всегда заключайте в двойные кавычки замену переменных «$ foo»
и подстановку команд «$ (foo)»
. Таким образом:
if [ -n "$1" ]; then …
В bash, ksh и zsh, но не в обычном sh, вы также можете использовать двойные скобки. Одиночные скобки […]
анализируются как обычная команда (и действительно [
- обычная команда, хотя обычно встроенная). Двойные скобки - это отдельная синтаксическая конструкция, и в большинстве случаев вы можете опускать двойные кавычки.
if [[ -n $1 ]]; then …
Однако есть исключение: [["$ foo" = "$ bar"]]
, чтобы проверить, имеют ли две переменные одинаковое значение, требуются двойные кавычки вокруг $ bar
], иначе $ bar
интерпретируется как шаблон подстановки. Опять же, вместо того, чтобы запоминать детали, вы можете все время использовать двойные кавычки.
Вы не можете использовать IFS в bash для разделения ни на что (он должен быть на символе ). Между r
и d
в rd
нет символов. Отсутствие пробела и отсутствие символа — это не то же самое, что нулевой символ.
Если вы хотите, чтобы каждый символ был отдельным элементом в массиве, я могу придумать один из способов: прочитать каждый символ по отдельности и добавить его в массив (и использовать IFS для избавления от пробелов и-
):
bash-4.4$ while IFS=' -' read -n1 c ; do [[ -n $c ]] && foo+=("$c"); done <<<"-w o rd"
bash-4.4$ declare -p foo
declare -a foo=([0]="w" [1]="o" [2]="r" [3]="d")
Вы можете удалить ненужные символы с помощью${var//pattern/replacement}
:
s='-w o rd'
s=${s//[- ]}
А затем используйте расширение подстроки для выбора одного символа за раз:
$ for ((i=0; i < ${#s}; i++)); do
echo ${s:i:1}; # or do whatever you like here
done
(похоже, он работает даже с многобайтовыми -символами, по крайней мере, в моей системе.)
Обратите внимание, что IFS='\0- '
будет назначать буквальную обратную косую черту для IFS
, поскольку обратная косая черта не имеет особого значения в одинарных кавычках. Восьмеричный escape будет работать внутри $'...'
, но переменные в Bash не могут содержать байт NUL, так что это не помогает. (Строка обрезается на NUL, т.е. x=$'foo\0bar'; printf "%q\n" "$x"
печатает только foo
. Кроме того, как сказал @muru, разделение на байт NUL — это не то же самое, что разделение между каждым символом)
Если ваша строка не слишком длинная:
w='-w o rd baa'
w=${w//[!a-z]} # strip anything but lower case letters
eval echo '${w:'{0..10}':1}'
eval array=\( '${w:'{0..10}':1}' \)
echo "${array[@]}"
w o r d b a a
Вы можете превратить глупую вещь во что-то более неприятное, безопасное для IFS -и glob -, которое учитывает различия в относительном порядке фигурных скобок и расширений переменных между zsh
, bash
иksh
:
args(){ printf '<%s> ' "$@"; echo; }
w='-e a * () \n peek
fo*"x'"'q"
eval eval args "'\"\${w:'{0..$((${#w}-1))}':1}\"'"
eval eval 'array=\(' "'\"\${w:'{0..$((${#w}-1))}':1}\"'" '\)'
args "${array[@]}"
<-> <e> < > <a> < > <*> < > <(> <)> < > <\> <n> < > <p> <e> <e> <k> <
> < > <f> <o> <*> <"> <x> <'> <q>
Но, откровенно говоря, единственная польза от такого чудовища может заключаться в том, чтобы отпугнуть новичков и произвести впечатление на дураков предполагаемым глубоким знанием тонкостей языка оболочки;-)