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 printfs 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 printfimprime (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
printfd » %cpourraient ê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
printfré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 zshsi vous utilisez le tableau match[]au lieu de BASH_REMATCH[], et soustrayez 1 de tous les index car zshne conserve pas un élément 0 avec la correspondance entière.
sedj'ai essayé d'abord que je pouvais me donner un coup de pied.