Comment insérer un espace tous les quatre caractères sur une longue ligne?

30

J'ai une longue ligne que je veux insérer un espace tous les 4 caractères, sur une seule ligne de texte solide pour en faciliter la lecture, quelle est la façon la plus simple de le faire? je devrais aussi pouvoir entrer la ligne à partir d'un tuyau. par exemple

echo "foobarbazblargblurg" | <some command here>

donne

foob arba zbla rgbl urg
xénoterracide
la source

Réponses:

54

Utilisez sed comme suit:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
dogbane
la source
1
maudissant qui était si proche du sedj'ai essayé d'abord que je pouvais me donner un coup de pied.
xenoterracide
7
Juste curieux, qu'est-ce que le «&» accomplit? Oh, c'est un standin pour «la chose qui vient de correspondre». Que je suis bête.
Omnifarious
1
il convient de noter que cela ajoute également un espace à la fin s'il y a un caractère de plus dans la chaîne, ce qui pourrait ne pas être souhaitable
Anubis
@Anubis's/.\{4\}/& /g;s/ $//'
wieczorek1990
21

Vous pouvez utiliser l'exemple simple suivant:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
Kenorb
la source
Très bien ... Je pense que c'est encore mieux que la sedréponse. Je ne savais pas foldavant.
Wildcard
1
Malheureusement, avec les versions actuelles de GNU fold, cela ne fonctionne pas avec les caractères multi-octets (comme echo €€€€€€€€ | fold -w4 | paste -sd' ' -dans UTF-8).
Stéphane Chazelas
3

Voici l'exemple utilisant grepet xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
Kenorb
la source
xargss'exécute echopar défaut, donc il ne fonctionnera pas avec des mots comme -nenou qui contiennent des barres obliques inverses selon l' echoimplémentation. Vous verrez également le caractère de nouvelle ligne étrange de temps en temps si xargs en exécute plusieurs echo. Mieux vaut y aller à la paste -sd ' ' -place. Notez que ce -on'est pas une option standard.
Stéphane Chazelas
3

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.

mr.spuratic
la source
3

Avec zshseulement:

str=foobarbazblargblurg

set -o extendedglob
printf '%s\n' ${str//(#m)????/$MATCH }

Ou

printf '%s%s%s%s ' ${(s::)str}

avec ksh93seulement:

printf '%s\n' "${str//????/\0 }"

Avec n'importe quel shell POSIX uniquement (évitant également l'espace de fin si la longueur d'entrée est un multiple de 4):

out=
while true; do
  case $str in
    (?????*)
      new_str=${str#????}
      out=$out${str%"$new_str"}' '
      str=$new_str
      ;;
    (*)
      out=$out$str
      break
  esac
done
printf '%s\n' "$out"

Maintenant, c'est pour les personnages . Si vous vouliez le faire sur des grappes de graphèmes (par exemple, casser Stéphane, écrit comme $'Ste\u0301phane', comme Stép haneet non Ste phan e), avec zsh:

set -o rematchpcre
str=$'Ste\u301phane' out=
while [[ $str =~ '(\X{4})(.+)' ]] {
  out+="$match[1] " str=$match[2]
}
out+=$str
printf '%s\n' $out

Avec ksh93, vous pouvez également découper par largeur d'affichage, ce qui fonctionnerait pour cela Stéphaneci - dessus, mais pourrait également aider lorsque d'autres types de caractères de largeur nulle ou double largeur sont impliqués:

str=$'Ste\u301phane' out=
while
  start=${ printf %L.4s. "$str"; }
  start=${start%.}
  [ "$start" != "$str" ]
do
  out+="$start " str=${str#"$start"}
done
out+=$str
printf '%s\n' "$out"
Stéphane Chazelas
la source
2

Je vais répondre en insérant uniquement des espaces comme requis afin qu'un espace apparaisse au moins tous les 4 caractères sur une ligne; Je ne sais pas de quelle manière vous voulez gérer ce cas. Par exemple, étant donné l'entrée de "aa bbccdd", vous obtiendrez la sortie "aa bbcc dd" plutôt que "aa b bccd d".

J'utilise Perl pour l'anticipation, mais je ne connais pas très bien Perl en général, il peut donc y avoir des ajustements nécessaires:

$ echo "foobarbazblargblurg" | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
foob arba zbla rgbl urg

$ echo 'aa bbccdd' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
aa bbcc dd
# not 'aa b bccd d'!

$ echo 'some input' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
some inpu t
# not 'some  inp ut'!

$ echo $'aabb\nc cddee' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g' | 
> while read; do echo "${REPLY}x"; done
aabbx
c cdde ex
# no spaces added at the end of the first line (while loop to add to the end of
# the line and show this)
Fred Nurk
la source
0

Je l'ai fait en utilisant python

Je lis d'abord le fichier, puis je divise par 4 caractères et j'ajoute de l'espace

#!/usr/bin/python
import re
b=re.compile(r'[a-z]{4}')

p=open('/root/l.txt','r')
i=p.readlines()
for j in i:
    m=re.findall(b,j)
print " " .join (m) + "  "

/root/l.txt ==> Se compose du contenu que vous avez donné dans l'exemple

sortie

foob arba zbla rgbl
Praveen Kumar BS
la source