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