Как разделить строку на буквы с помощью IFS и прочитать без пробела / нуля, пробела и символа -?

Несогласованность во многом объясняется историческими причинами.

Использование квадратных скобок в качестве условной команды произошло после использования скобок в шаблонах подстановочных знаков. Итак, в то время, когда [-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 интерпретируется как шаблон подстановки. Опять же, вместо того, чтобы запоминать детали, вы можете все время использовать двойные кавычки.

3
15.12.2018, 12:21
3 ответа

Вы не можете использовать 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")
6
27.01.2020, 21:10

Вы можете удалить ненужные символы с помощью${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 — это не то же самое, что разделение между каждым символом)

2
27.01.2020, 21:10

Если ваша строка не слишком длинная:

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>    

Но, откровенно говоря, единственная польза от такого чудовища может заключаться в том, чтобы отпугнуть новичков и произвести впечатление на дураков предполагаемым глубоким знанием тонкостей языка оболочки;-)

1
27.01.2020, 21:10

Теги

Похожие вопросы