В качестве альтернативы итерации по 0 .. ${#string}-1
с циклом for / while, есть два других способа сделать это с only bash : использовать =~
и использовать printf
,(Существует третья возможность использования eval
и выражения последовательности {..}
, но в этом нет ясности.)
При правильном окружении и включенном NLS в bash они будут работать с не-ASCII, как и ожидалось, удаляя потенциалисточники сбоя со старыми системными инструментами, такими как sed
, если это проблема.Они будут работать с bash-3.0 (выпущен в 2005 году).
Использование =~
и регулярных выражений, преобразование строки в массив в одном выражении:
string="wonkabars"
[[ "$string" =~ ${string//?/(.)} ]] # splits into array
printf "%s\n" "${BASH_REMATCH[@]:1}" # loop free: reuse fmtstr
declare -a arr=( "${BASH_REMATCH[@]:1}" ) # copy array for later
Как это работаетдолжен выполнить расширение string
, которое заменяет каждый отдельный символ на (.)
, а затем сопоставить это сгенерированное регулярное выражение с группировкой для захвата каждого отдельного символа в BASH_REMATCH[]
.Индекс 0 установлен на всю строку, так как этот специальный массив доступен только для чтения, вы не можете удалить его, обратите внимание на :1
, когда массив расширяется, чтобы пропустить индекс 0, если это необходимо.Некоторое быстрое тестирование нетривиальных строк (> 64 символов) показывает, что этот метод на существенно быстрее, чем метод, использующий операции bash для строк и массивов.
Выше будет работать со строками, содержащими символы новой строки, =~
поддерживает POSIX ERE, где .
соответствует всему, кроме NUL по умолчанию, т.е. регулярное выражение компилируется без REG_NEWLINE
.(Поведение обработки текста POSIX Утилиты допускается отличаться по умолчанию в этом отношении, и обычно это так.)
Второй вариант, используя printf
:
string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do
((xx)) && printf "\n" || break
done
Этот цикл увеличивает индекс ii
для печати по одному символу за раз и прерывается, когда не осталось символов.Это было бы еще проще, если бы bash printf
возвращал количество напечатанных символов (как в C), а не состояние ошибки, вместо этого количество напечатанных символов фиксируется в xx
с использованием %n
.(Это работает по крайней мере до bash-2.05b.)
С bash-3.1 и printf -v var
у вас немного больше гибкости, и вы можете избежать падения с конца строки, если вы что-то делаетекроме печати символов, например, для создания массива:
declare -a arr
ii=0
while printf -v cc "%s%n" "${string:(ii++):1}" xx; do
((xx)) && arr+=("$cc") || break
done