En bash uniquement, aucune commande externe:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
ou en version tube à une ligne:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
La façon dont cela fonctionne consiste à convertir chaque caractère de la chaîne en "(.)" Pour la correspondance regex et la capture avec =~
, puis il suffit de sortir les expressions capturées à partir du BASH_REMATCH[]
tableau, groupées selon les besoins. Les espaces de début / de fin / intermédiaires sont conservés, supprimez les guillemets "${BASH_REMATCH[@]:1}"
pour les supprimer.
Ici, il est enveloppé dans une fonction, celle-ci traitera ses arguments ou lira stdin s'il n'y a pas d'arguments:
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
Vous pouvez facilement paramétrer le nombre pour ajuster la chaîne de format en conséquence.
Un espace de fin est ajouté, utilisez deux printf
s au lieu d'un si c'est un problème:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Le premier printf
imprime (jusqu'à) les 4 premiers caractères, le second imprime conditionnellement tout le reste (le cas échéant) avec un espace de tête pour séparer les groupes. Le test porte sur 5 éléments et non sur 4 pour tenir compte de l'élément zéro.
Remarques:
- coquille
printf
d » %c
pourraient être utilisés à la place %s
, %c
(peut - être) rend l'objet plus claire, mais pas de caractères multi-octets. Si votre version de bash est capable, tout ce qui précède est sûr pour les caractères multi-octets.
- shell
printf
réutilise sa chaîne de format jusqu'à ce qu'il soit à court d'arguments, donc il engloutit simplement 4 arguments à la fois et gère les arguments de fin (donc aucun cas de bord nécessaire, contrairement à certaines des autres réponses ici qui sont sans doute fausses)
BASH_REMATCH[0]
est toute la chaîne correspondante, donc uniquement la sortie à partir de l'index 1
- utiliser à la
printf -v myvar ...
place pour stocker dans une variable myvar
(sous réserve du comportement habituel en boucle de lecture / sous-shell)
- ajouter
printf "\n"
si nécessaire
Vous pouvez faire fonctionner ce qui précède zsh
si vous utilisez le tableau match[]
au lieu de BASH_REMATCH[]
, et soustrayez 1 de tous les index car zsh
ne conserve pas un élément 0 avec la correspondance entière.
sed
j'ai essayé d'abord que je pouvais me donner un coup de pied.