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 printfs 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 printfdrukuje (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
printfS” %cmoż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
printfponownie 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, zshjeśli match[]zamiast tego użyjesz tablicy BASH_REMATCH[]i odejmiesz 1 od wszystkich indeksów, ponieważ zshnie zachowuje elementu 0 z całym dopasowaniem.
sedże spróbowałem najpierw, że mogłem się kopnąć.