Comment diviser une chaîne en plusieurs chaînes séparées par au moins un espace dans le shell bash?

J’ai une chaîne contenant beaucoup de mots avec au moins un espace entre deux. Comment puis-je diviser la chaîne en mots individuels pour pouvoir les parcourir en boucle?

La chaîne est passée en argument. Par exemple ${2} == "cat cat file" . Comment puis-je le parcourir en boucle?

Comment puis-je vérifier si une chaîne contient des espaces?

Avez-vous essayé de simplement passer la variable ssortingng à une boucle for ? Bash, pour sa part, se divisera automatiquement sur les espaces.

 sentence="This is a sentence." for word in $sentence do echo $word done 

 This is a sentence. 

J’aime la conversion en tableau, pour pouvoir accéder à des éléments individuels:

  sentence="this is a story" ssortingngarray=($sentence) 

maintenant vous pouvez accéder directement aux éléments individuels (il commence par 0):

  echo ${ssortingngarray[0]} 

ou reconvertir en chaîne afin de boucler:

  for i in "${ssortingngarray[@]}" do : # do whatever on $i done 

Bien sûr, la mise en boucle directe de la chaîne a déjà été traitée auparavant, mais cette réponse avait l’inconvénient de ne pas suivre les éléments individuels pour une utilisation ultérieure:

  for i in $sentence do : # do whatever on $i done 

Voir aussi Référence de Bash Array

Utilisez simplement les shells “set” intégrés. Par exemple,

 définir $ text

Après cela, les mots individuels dans $ text seront en 1 $, 2 $, 3 $, etc.

 set - junk $ text
 décalage

pour gérer le cas où $ text est vide ou commence par un tiret. Par exemple:

 text = "Ceci est un test"
 set - junk $ text
 décalage
 pour le mot  faire
   echo "[$ word]"
 terminé

Ceci imprime

 [Ce]
 [est]
 [une]
 [tester]

Le moyen le plus simple et le plus sûr de BASH 3 et supérieur est le suivant:

 var="ssortingng to split" read -ra arr <<<"$var" 

(où arr est le tableau qui prend les parties fractionnées de la chaîne) ou, s'il peut y avoir de nouvelles lignes dans l'entrée et que vous souhaitez plus que la première ligne:

 var="ssortingng to split" read -ra arr -d '' <<<"$var" 

(notez s'il vous plaît l'espace dans -d '' , il ne peut pas être laissé de côté), mais cela pourrait vous donner une nouvelle ligne inattendue de <<<"$var" (car cela ajoute implicitement un LF à la fin).

Exemple:

 touch NOPE var="* a *" read -ra arr <<<"$var" for a in "${arr[@]}"; do echo "[$a]"; done 

Sort le prévu

 [*] [a] [*] 

comme cette solution (contrairement à toutes les solutions précédentes ici) n'est pas sujette à un glissement de shell inattendu et souvent incontrôlable.

Cela vous donne également la pleine puissance de l'IFS comme vous le souhaitez:

Exemple:

 IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd) for a in "${arr[@]}"; do echo "[$a]"; done 

Sort quelque chose comme:

 [tino] [x] [1000] [1000] [Valentin Hilbig] [/home/tino] [/bin/bash] 

Comme vous pouvez le voir, les espaces peuvent aussi être préservés de la manière suivante:

 IFS=: read -ra arr <<<' split : this ' for a in "${arr[@]}"; do echo "[$a]"; done 

les sorties

 [ split ] [ this ] 

Veuillez noter que le traitement de IFS dans BASH est un sujet à part entière, faites donc vos tests, quelques sujets intéressants à ce sujet:

  • unset IFS défini: Ignore les exécutions de SPC, TAB, NL et les débuts et fins de ligne
  • IFS='' : Pas de séparation de champ, lit tout
  • IFS=' ' : exécutions de SPC (et SPC uniquement)

Un dernier exemple

 var=$'\n\nthis is\n\n\na test\n\n' IFS=$'\n' read -ra arr -d '' <<<"$var" i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done 

les sorties

 1 [this is] 2 [a test] 

tandis que

 unset IFS var=$'\n\nthis is\n\n\na test\n\n' read -ra arr -d '' <<<"$var" i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done 

les sorties

 1 [this] 2 [is] 3 [a] 4 [test] 

BTW:

  • Si vous n'avez pas l'habitude de vous habituer à $'ANSI-ESCAPED-STRING' vous gagnerez du temps.

  • Si vous n'incluez pas -r (comme dans read -a arr <<<"$var" ), alors read est une barre oblique inverse. Ceci est laissé comme exercice pour le lecteur.


Pour la deuxième question:

Pour tester quelque chose dans une chaîne, je m'en tiens généralement à la case , car cela permet de vérifier plusieurs cas à la fois (note: la casse exécute uniquement la première correspondance, si vous avez besoin de multiplier les déclarations de case ). (jeu de mots intentionnel):

 case "$var" in '') empty_var;; # variable is empty *' '*) have_space "$var";; # have SPC *[[:space:]]*) have_whitespace "$var";; # have whitespaces like TAB *[^-+.,A-Za-z0-9]*) have_nonalnum "$var";; # non-alphanum-chars found *[-+.,]*) have_punctuation "$var";; # some punctuation chars found *) default_case "$var";; # if all above does not match esac 

Donc, vous pouvez définir la valeur de retour pour vérifier SPC comme ceci:

 case "$var" in (*' '*) true;; (*) false;; esac 

Pourquoi case Parce que c'est généralement un peu plus lisible que les séquences de regex, et grâce aux métacaractères Shell, il gère très bien 99% de tous les besoins.

 $ echo "This is a sentence." | tr -s " " "\012" This is a sentence. 

Pour vérifier les espaces, utilisez grep:

 $ echo "This is a sentence." | grep " " > /dev/null $ echo $? 0 $ echo "Thisisasentence." | grep " " > /dev/null $ echo $? 1 

(A) Pour diviser une phrase en ses mots (espace séparé), vous pouvez simplement utiliser l’IFS par défaut en utilisant

 array=( $ssortingng ) 

Exemple d’ exécution de l’extrait de code suivant

 #!/bin/bash sentence="this is the \"sentence\" 'you' want to split" words=( $sentence ) len="${#words[@]}" echo "words counted: $len" printf "%s\n" "${words[@]}" ## print array 

va sortir

 words counted: 8 this is the "sentence" 'you' want to split 

Comme vous pouvez le voir, vous pouvez utiliser des guillemets simples ou doubles sans aucun problème

Remarques:
– c’est fondamentalement la même chose que la réponse de mob , mais de cette façon, vous stockez le tableau pour tout autre besoin. Si vous n’avez besoin que d’une seule boucle, vous pouvez utiliser sa réponse, qui est une ligne plus courte 🙂
– Veuillez vous reporter à cette question pour connaître les autres méthodes permettant de diviser une chaîne en fonction du délimiteur.

(B) Pour rechercher un caractère dans une chaîne, vous pouvez également utiliser une correspondance d’expression régulière.
Exemple pour vérifier la présence d’un caractère d’espace que vous pouvez utiliser:

 regex='\s{1,}' if [[ "$sentence" =~ $regex ]] then echo "Space here!"; fi 

Pour vérifier les espaces simplement avec bash:

 [[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"