Только в bash, без внешних команд:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
или в виде однолинейной трубы:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
Это работает путем преобразования каждого символа строки в "(.)" Для сопоставления с регулярным выражением и захвата с помощью =~, а затем просто выведите захваченные выражения из BASH_REMATCH[]массива, сгруппированного по мере необходимости Ведущие / конечные / промежуточные пробелы сохраняются, удаляйте кавычки вокруг"${BASH_REMATCH[@]:1}" чтобы пропустить их.
Здесь она заключена в функцию, она будет обрабатывать свои аргументы или читать stdin, если аргументов нет:
function fmt4() {
while IFS= read -r str; do
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}
$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg
Вы можете легко параметризировать счетчик, чтобы соответствующим образом настроить строку формата.
Добавляется завершающий пробел, используйте два printfs вместо одного, если это проблема:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Первый printf печатает (до) первых 4 символов, второй условно печатает все остальные (если они есть) с пробелом для разделения групп. Тест для 5 элементов, а не 4 для учета нулевого элемента.
Заметки:
- Shell
printf«s %cможет быть использован вместо %s, %c(возможно) делает цель более ясной, но это не многобайтный символ безопасности. Если ваша версия bash способна, все вышеизложенное безопасно для многобайтовых символов.
- shell
printfиспользует свою строку формата до тех пор, пока у нее не закончатся аргументы, поэтому она просто поглощает 4 аргумента за раз и обрабатывает завершающие аргументы (поэтому не требуется крайних случаев, в отличие от некоторых других ответов, которые здесь могут быть ошибочными)
BASH_REMATCH[0] является всей совпавшей строкой, поэтому выводится только начиная с индекса 1
printf -v myvar ...вместо этого используйте для сохранения в переменной myvar(в зависимости от обычного поведения цикла чтения / подоболочки)
- добавить,
printf "\n"если требуется
Вы можете заставить вышеописанное работать, zshесли вы используете массив match[]вместо BASH_REMATCH[], и вычитаете 1 из всех индексов, так как zshне сохраняете элемент 0 со всем соответствием.
sedя попробовал первым, я мог ударить себя.