Лише в 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 здатна, перераховане вище для всіх багатобайтових символів.
- оболонки
printf
повторно використовує рядок формату, поки у неї не вистачає аргументів, тому вона просто збиває 4 аргументи за один раз і обробляє зворотні аргументи (тому не потрібні крайові випадки, на відміну від деяких інших відповідей тут, які, мабуть, неправильні)
BASH_REMATCH[0]
- це весь збігований рядок, тому тільки вихід, починаючи з індексу 1
- використовувати
printf -v myvar ...
замість цього для зберігання змінноїmyvar
(залежно від звичайної поведінки читання циклу / підрозділу)
- додати,
printf "\n"
якщо потрібно
Ви можете зробити вищезгадану роботу в тому zsh
випадку, якщо ви використовуєте масив match[]
замість BASH_REMATCH[]
, і віднімете 1 з усіх індексів, так як zsh
не зберігається 0 елемент за весь збіг.
sed
я спробував спочатку, я міг бити себе ногами.