web-dev-qa-db-fra.com

Dans bash, comment puis-je vérifier si une chaîne commence par une valeur?

Je voudrais vérifier si une chaîne commence par "noeud" par exemple. "node001". Quelque chose comme

if [ $Host == user* ]  
  then  
  echo yes  
fi

Comment puis-je le faire correctement?


Je dois en outre combiner des expressions pour vérifier si l'hôte est "utilisateur1" ou commence par "nœud"

if [ [[ $Host == user1 ]] -o [[ $Host == node* ]] ];  
then  
echo yes 
fi

> > > -bash: [: too many arguments

Comment le faire correctement?

644
Tim

Cet extrait de Guide de rédaction avancée de scripts Bash dit:

# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.

[[ $a == z* ]]   # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

Donc, vous aviez presque correct; vous aviez besoin de doubles crochets, pas de crochets simples.


En ce qui concerne votre deuxième question, vous pouvez l'écrire comme suit:

Host=user1
if  [[ $Host == user1 ]] || [[ $Host == node* ]] ;
then
    echo yes1
fi

Host=node001
if [[ $Host == user1 ]] || [[ $Host == node* ]] ;
then
    echo yes2
fi

Qui fera écho

yes1
yes2

La syntaxe de if de Bash est difficile à prendre en main (IMO).

914
Mark Rushakoff

Si vous utilisez un bash récent (v3 +), je suggère à l'opérateur de comparaison bash regex =~, c'est-à-dire.

if [[ "$Host" =~ ^user.* ]]; then
    echo "yes"
fi

Pour faire correspondre this or that dans une expression rationnelle, utilisez |, c.-à-d.

if [[ "$Host" =~ ^user.*|^Host1 ]]; then
    echo "yes"
fi

Remarque - il s'agit de la syntaxe "correcte" des expressions régulières.

  • user* signifie use et aucune ou plusieurs occurrences de r, de sorte que use et userrrr correspondent.
  • user.* signifie user et aucune ou plusieurs occurrences de caractères, de sorte que user1, userX correspondra.
  • ^user.* signifie correspondre au motif user.* au début de $ Host.

Si vous n'êtes pas familier avec la syntaxe des expressions régulières, essayez de vous référer à cette ressource .

Remarque: il est préférable de poser chaque nouvelle question comme une nouvelle question, cela rend le stackoverflow plus ordonné et plus utile. Vous pouvez toujours inclure un lien vers une question précédente pour référence.

174
brabster

J'essaie toujours de rester avec POSIX sh au lieu d'utiliser des extensions bash, car l'un des principaux points du script est la portabilité. (Outre connexion programmes, ne les remplace pas)

En sh, il existe un moyen simple de rechercher une condition "is-prefix".

case $Host in node*)
    your code here
esac

Compte tenu de l'âge, du style mystérieux et cruel (et bash n'est pas le remède: c'est plus compliqué, moins cohérent et moins portable), j'aimerais souligner un aspect fonctionnel très agréable: alors que certains éléments syntaxiques comme case sont intégrés, les constructions résultantes ne sont pas différentes de tout autre travail. Ils peuvent être composés de la même manière:

if case $Host in node*) true;; *) false;; esac; then
    your code here
fi

Ou même plus court

if case $Host in node*) ;; *) false;; esac; then
    your code here
fi

Ou même plus court (juste pour présenter ! comme un élément de langage - mais c'est un mauvais style maintenant)

if ! case $Host in node*) false;; esac; then
    your code here
fi

Si vous aimez être explicite, construisez votre propre élément de langage:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

N'est-ce pas vraiment très gentil?

if beginswith node "$Host"; then
    your code here
fi

Et comme sh n’est en gros que des emplois et des listes de chaînes (et des processus internes, à partir desquels les emplois sont composés), nous pouvons maintenant même faire de la programmation fonctionnelle légère:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }

all() {
    test=$1; shift
    for i in "$@"; do
        $test "$i" || return
    done
}

all "beginswith x" x xy xyz ; checkresult  # prints TRUE
all "beginswith x" x xy abc ; checkresult  # prints FALSE

C'est élégant. Non pas que je recommande l'utilisation de sh pour quelque chose de sérieux - cela casse trop vite pour répondre aux exigences du monde réel (pas de lambda, vous devez donc utiliser des chaînes. Mais les appels de fonctions imbriquées avec des chaînes ne sont pas possibles, les tuyaux ne sont pas possibles ...)

116
Jo So

Vous pouvez sélectionner uniquement la partie de la chaîne que vous souhaitez vérifier:

if [ ${Host:0:4} = user ]

Pour votre question suivante, vous pouvez utiliser un OU :

if [[ $Host == user1 || $Host == node* ]]
68
martin clayton

Je préfère les autres méthodes déjà postées, mais certaines personnes aiment utiliser:

case "$Host" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

Edit:

J'ai ajouté vos suppléants à la déclaration de cas ci-dessus

Dans votre version modifiée, vous avez trop de crochets. Ça devrait ressembler à ça:

if [[ $Host == user1 || $Host == node* ]];
55
Dennis Williamson

Bien que je trouve la plupart des réponses correctes, beaucoup d’entre elles contiennent des bashismes inutiles. expansion du paramètre POSIX vous donne tout ce dont vous avez besoin:

[ "${Host#user}" != "${Host}" ]

et

[ "${Host#node}" != "${Host}" ]

${var#expr} supprime le plus petit préfixe correspondant à expr de ${var} et le renvoie. Par conséquent, si ${Host} ne commence pas par user (node), ${Host#user} (${Host#node}) est identique à ${Host}.

expr permet fnmatch() caractères génériques, donc ${Host#node??} et amis fonctionnent également.

29
dhke

puisque # a une signification dans Bash, je suis arrivé à la solution suivante.
En outre, j'aime mieux emballer les chaînes avec "" pour surmonter les espaces, etc.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "skip comment line"
fi
29
ozma

Ajouter un peu plus de détails de syntaxe à la réponse de rang le plus élevé de Mark Rushakoff.

L'expression

$Host == node*

Peut aussi être écrit comme

$Host == "node"*

L'effet est le même. Assurez-vous simplement que le caractère générique est en dehors du texte cité. Si le caractère générique est à l'intérieur les guillemets, il sera interprété littéralement (c'est-à-dire pas comme un caractère générique).

8
MrPotatoHead

@OP, vous pouvez utiliser case/esac pour vos deux questions

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac

deuxième question

case "$Host" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$Host" in
 node*|user) echo "ok";;
esac

OU Bash 4.0

case "$Host" in
 user) ;& 
 node*) echo "ok";; 
esac
8
ghostdog74
if [ [[ $Host == user1 ]] -o [[ $Host == node* ]] ];  
then  
echo yes 
fi

ne fonctionne pas, car tous [, [[ et test reconnaît la même grammaire non récursive. voir la section EXPRESSIONS CONDITIONNELLES dans votre page de manuel bash.

En aparté, le SUSv3 dit

La commande conditionnelle dérivée de KornShell (double crochet [[]]) a été supprimée de la description du langage de commande Shell dans une première proposition. Des objections ont été soulevées selon lesquelles le problème réel est l’utilisation abusive de la commande test ([), et son insertion dans le shell est la mauvaise façon de résoudre le problème. Au lieu de cela, une documentation appropriée et un nouveau mot de commande réservé au shell (!) sont suffisants.

Les tests nécessitant plusieurs tests peuvent être effectués au niveau du shell à l'aide d'appels individuels du test commande et logiques Shell, plutôt que d’utiliser le drapeau - o, sujet aux erreurs, de test .

vous auriez besoin de l'écrire de cette façon, mais test ne le prend pas en charge:

if [ $Host == user1 -o $Host == node* ];  
then  
echo yes 
fi

test utilise = pour l'égalité des chaînes, plus important encore, il ne prend pas en charge correspondance de modèle.

case/esac supporte bien la correspondance des modèles:

case $Host in
user1|node*) echo yes ;;
esac

cela a l'avantage supplémentaire de ne pas dépendre de bash, la syntaxe est portable. à partir de la spécification Single Unix, le langage de commande Shell:

case Word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac
6
just somebody

grep

Oubliant les performances, voici POSIX et c'est plus joli que les solutions case:

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

Explication:

J'ai modifié la réponse de @ markrushakoff pour en faire une fonction appelable:

function yesNo {
  # prompts user with $1, returns true if response starts with y or Y or is empty string
  read -e -p "
$1 [Y/n] " YN

  [[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}

Utilisez-le comme ceci:

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] Y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] yes
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n]
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] ddddd
false

Voici une version plus complexe qui fournit une valeur par défaut spécifiée:

function toLowerCase {
  echo "$1" | tr '[:upper:]' '[:lower:]'
}

function yesNo {
  # $1: user Prompt
  # $2: default value (assumed to be Y if not specified)
  # Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string

  local DEFAULT=yes
  if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
  if [[ "$DEFAULT" == y* ]]; then
    local Prompt="[Y/n]"
  else
    local Prompt="[y/N]"
  fi
  read -e -p "
$1 $Prompt " YN

  YN="$( toLowerCase "$YN" )"
  { [ "$YN" == "" ] && [[ "$Prompt" = *Y* ]]; } || [[ "$YN" = y* ]]
}

Utilisez-le comme ceci:

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N]
false

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N] y
true

$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false
1
Mike Slinn