J'ai une chaîne contenant beaucoup de mots avec au moins un espace entre chaque 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 faire une boucle?
Aussi, comment puis-je vérifier si une chaîne contient des espaces?
Avez-vous essayé de passer simplement la variable chaîne à une boucle for
? Bash, pour sa part, se scinde automatiquement sur les espaces blancs.
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"
stringarray=($sentence)
vous pouvez maintenant accéder directement à des éléments individuels (cela commence par 0):
echo ${stringarray[0]}
ou reconvertir en chaîne afin de boucler:
for i in "${stringarray[@]}"
do
:
# do whatever on $i
done
Bien sûr, la réponse directe en boucle à travers la chaîne a déjà été répondue, mais cette réponse présentait 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 du tableau de Bash .
Il suffit d’utiliser les "coquilles" intégrées. Par exemple,
set $ text
Après cela, les mots individuels dans $ text seront dans $ 1, $ 2, $ 3, etc. Pour la robustesse, on fait habituellement
set - junk $ text shift
pour gérer le cas où $ text est vide ou commencer par un tiret. Par exemple:
text = "Ceci est un test" set - junk $ text shift pour Word; Est-ce que fait écho "[$ Word]" fait
Cela imprime
[C'est un test]
Le moyen probablement le plus facile et le plus sûr dans BASH 3 et supérieur est:
var="string 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 des retours à la ligne dans l'entrée et que vous voulez plus que la première ligne:
var="string to split"
read -ra arr -d '' <<<"$var"
(notez s'il vous plaît l'espace dans -d ''
, il ne peut pas être laissé), mais cela pourrait vous donner une nouvelle ligne inattendue de <<<"$var"
(car ceci ajoute implicitement un LF à la fin) .
Exemple:
touch NOPE
var="* a *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done
Sortie attendue
[*]
[a]
[*]
car cette solution (contrairement à toutes les solutions précédentes ici) n’est pas sujette à un éblouissement inattendu et souvent incontrôlable de Shell.
En outre, cela vous donne toute la puissance d'IFS que vous souhaitez probablement:
Exemple:
IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done
Sortie quelque chose comme:
[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]
Comme vous pouvez le constater, les espaces peuvent également être préservés:
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 en soi, alors faites vos tests, quelques sujets intéressants à ce sujet:
unset IFS
: Ignore les exécutions de SPC, TAB, NL et commence et se termine en ligneIFS=''
: Pas de séparation de champs, lit toutIFS=' '
: Exécution 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'êtes pas habitué à $'ANSI-ESCAPED-STRING'
vous y habituer, vous gagnerez du temps.
Si vous n'incluez pas -r
(comme dans read -a arr <<<"$var"
), la lecture est supprimée. 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 à case
, car cela permet de vérifier plusieurs observations à la fois (remarque: la casse n'exécute que la première correspondance, si vous avez besoin d'une instruction complémentaire, multiplce case
,) et ce besoin est assez souvent le cas (jeu de mots voulu):
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
? Comme il est généralement un peu plus lisible que les séquences regex, et grâce aux métacaractères de Shell, il gère très bien 99% des 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 mots (séparés par des espaces), vous pouvez simplement utiliser l'IFS par défaut en utilisant
array=( $string )
Exemple exécutant 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 constater, vous pouvez aussi utiliser des guillemets simples ou doubles sans problème.
Notes:
- Ceci est fondamentalement la même chose que la réponse de mob , mais de cette manière, vous stockez le tableau pour tout besoin ultérieur. Si vous n'avez besoin que d'une seule boucle, vous pouvez utiliser sa réponse, qui est une ligne plus courte :)
- veuillez vous référer à cette question pour connaître d'autres méthodes permettant de fractionner une chaîne en fonction d'un 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'espacement que vous pouvez utiliser:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
Pour vérifier les espaces juste avec bash:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"