Tylko w bash, brak zewnętrznych poleceń:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
lub jako wersja jednorurowa:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
Działa to w ten sposób, że konwertuje każdy znak ciągu na „(.)” W celu dopasowania wyrażenia regularnego i przechwytywania za pomocą =~
, a następnie po prostu wyprowadza przechwycone wyrażenia z BASH_REMATCH[]
tablicy, pogrupowane według potrzeb. Wiodące / końcowe / pośrednie spacje są zachowane, usuń cudzysłowy, "${BASH_REMATCH[@]:1}"
aby je pominąć.
Tutaj jest zawinięty w funkcję, ta przetworzy swoje argumenty lub odczyta stdin, jeśli nie ma żadnych argumentów:
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
Możesz łatwo sparametryzować liczbę, aby odpowiednio dostosować ciąg formatu.
Dodaje się końcowe miejsce, użyj dwóch printf
s zamiast jednego, jeśli jest to problem:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Pierwszy printf
drukuje (maksymalnie) pierwsze 4 znaki, drugi warunkowo drukuje wszystkie pozostałe (jeśli występują) z wiodącym odstępem, aby oddzielić grupy. Test obejmuje 5 elementów, a nie 4, które uwzględniają element zerowy.
Uwagi:
- Shell
printf
S” %c
może być stosowany zamiast %s
, %c
(być może) sprawia, że intencją jaśniejsze, ale to nie jest multi-byte bezpieczny charakter. Jeśli twoja wersja bash jest w stanie, powyższe jest bezpieczne dla znaków wielobajtowych.
- powłoka
printf
ponownie używa łańcucha formatu, dopóki nie zabraknie argumentów, więc po prostu pochłania 4 argumenty na raz i obsługuje końcowe argumenty (więc nie są potrzebne przypadki krawędziowe, w przeciwieństwie do niektórych innych odpowiedzi, które są prawdopodobnie błędne)
BASH_REMATCH[0]
to cały dopasowany ciąg, więc dane wyjściowe zaczynają się od indeksu 1
printf -v myvar ...
zamiast tego użyj do zapisania do zmiennej myvar
(z zastrzeżeniem zwykłego zachowania w pętli odczytu / podpowłoce)
- dodaj w
printf "\n"
razie potrzeby
Możesz włączyć powyższe, zsh
jeśli match[]
zamiast tego użyjesz tablicy BASH_REMATCH[]
i odejmiesz 1 od wszystkich indeksów, ponieważ zsh
nie zachowuje elementu 0 z całym dopasowaniem.
sed
że spróbowałem najpierw, że mogłem się kopnąć.