Только в 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
Вы можете легко параметризировать счетчик, чтобы соответствующим образом настроить строку формата.
Добавляется завершающий пробел, используйте два printf
s вместо одного, если это проблема:
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
я попробовал первым, я мог ударить себя.