it-swarm.com.de

Wie kann ich in Bash überprüfen, ob eine Zeichenfolge mit einem bestimmten Wert beginnt?

Ich möchte überprüfen, ob eine Zeichenfolge mit "node" beginnt, z. "node001". So etwas wie

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

Wie kann ich das richtig machen?


Ich muss außerdem Ausdrücke kombinieren, um zu überprüfen, ob Host entweder "user1" ist oder mit "node" beginnt.

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

> > > -bash: [: too many arguments

Wie mache ich es richtig?

644
Tim

Dieses Snippet im Advanced Bash Scripting Guide sagt:

# 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).

Sie hatten es also fast richtig; Sie brauchten doppelte Klammern, keine einfachen Klammern.


In Bezug auf Ihre zweite Frage können Sie es so schreiben:

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

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

Welches wird echo

yes1
yes2

Bashs if Syntax ist schwer zu gewöhnen (IMO).

914
Mark Rushakoff

Wenn Sie eine kürzliche Bash (v3 +) verwenden, empfehle ich den Bash-Regex-Vergleichsoperator =~, d. H.

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

Um this or that in einem regulären Ausdruck abzugleichen, verwenden Sie |, d.h.

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

Hinweis - Dies ist die richtige Syntax für reguläre Ausdrücke.

  • user* bedeutet use und null oder mehr Vorkommen von r, daher stimmen use und userrrr überein.
  • user.* bedeutet user und null oder mehr Vorkommen eines beliebigen Zeichens, daher stimmen user1, userX überein.
  • ^user.* bedeutet Übereinstimmung mit dem Muster user.* am Anfang von $ Host.

Wenn Sie mit der Syntax regulärer Ausdrücke nicht vertraut sind, lesen Sie diese Ressource .

Hinweis - Es ist besser, wenn Sie jede neue Frage als neue Frage stellen. Dadurch wird der Stapelüberlauf übersichtlicher und nützlicher. Sie können jederzeit einen Link zu einer vorherigen Frage als Referenz einfügen.

174
brabster

Ich versuche immer, mich an POSIX sh zu halten, anstatt Bash-Erweiterungen zu verwenden, da einer der Hauptaspekte der Skripterstellung die Portabilität ist. (Außerdem Verbinden Programme, die nicht ersetzt werden)

In sh gibt es eine einfache Möglichkeit, nach einer "is-prefix" -Zustand zu suchen.

case $Host in node*)
    your code here
esac

In Anbetracht dessen, wie alt, arkan und crufty sh ist (und Bash ist nicht die Heilung: Es ist komplizierter, weniger konsistent und weniger portabel), möchte ich auf einen sehr schönen funktionalen Aspekt hinweisen: Während einige Syntaxelemente wie case sind integriert, die resultierenden Konstrukte unterscheiden sich nicht von anderen Jobs. Sie können auf die gleiche Weise zusammengesetzt werden:

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

Oder noch kürzer

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

Oder sogar kürzer (nur um ! als Sprachelement zu präsentieren - aber das ist jetzt ein schlechter Stil)

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

Wenn Sie explizit sein möchten, erstellen Sie Ihr eigenes Sprachelement:

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

Ist das nicht wirklich ganz nett?

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

Und da es sich bei sh im Grunde nur um Jobs und String-Listen (und interne Prozesse, aus denen sich Jobs zusammensetzen) handelt, können wir jetzt sogar einige leichte funktionale Programmierungen durchführen:

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 "[email protected]"; do
        $test "$i" || return
    done
}

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

Das ist elegant. Nicht, dass ich es befürworten würde, sh für irgendetwas Ernstes zu verwenden - es bricht allzu schnell unter realen Bedingungen (keine Lambdas, daher müssen Strings verwendet werden. Es ist jedoch nicht möglich, Funktionsaufrufe mit Strings zu verschachteln, Pipes sind nicht möglich ...)

116
Jo So

Sie können nur den Teil der Zeichenfolge auswählen, den Sie überprüfen möchten:

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

Für Ihre Folgefrage können Sie ein ODER verwenden:

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

Ich bevorzuge die anderen Methoden, die bereits veröffentlicht wurden, aber einige Leute verwenden sie gerne:

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

Bearbeiten:

Ich habe Ihre Stellvertreter zur obigen case-Anweisung hinzugefügt

In Ihrer bearbeiteten Version haben Sie zu viele Klammern. Es sollte so aussehen:

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

Während ich die meisten Antworten hier ziemlich richtig finde, enthalten viele von ihnen unnötige Bashismen. POSIX-Parametererweiterung gibt Ihnen alles, was Sie brauchen:

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

und

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

${var#expr} entfernt das kleinste passende Präfix expr aus ${var} und gibt dieses zurück. Wenn also ${Host}nicht ​​mit user (node) beginnt, ist ${Host#user} (${Host#node}) dasselbe wie ${Host}.

expr erlaubt fnmatch() Platzhalter, daher funktionieren ${Host#node??} und Freunde auch.

29
dhke

da # in bash eine bedeutung hat, bin ich zu folgender lösung gekommen.
Außerdem packe ich lieber Strings mit "", um Leerzeichen usw. zu überwinden.

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

Mark Rushakoffs ranghöchste Antwort wurde um ein wenig mehr Syntaxdetails erweitert.

Der Ausdruck

$Host == node*

Kann auch geschrieben werden als

$Host == "node"*

Der Effekt ist der gleiche. Stellen Sie einfach sicher, dass sich der Platzhalter außerhalb des angegebenen Texts befindet. Wenn der Platzhalter innen ist, werden die Anführungszeichen wörtlich interpretiert (d. H. Nicht als Platzhalter).

8
MrPotatoHead

@OP, für beide Fragen kannst du case/esac verwenden

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

zweite Frage

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

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

ODER Bash 4.0

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

funktioniert nicht, da alle von [, [[ und test erkenne die gleiche nichtrekursive Grammatik. Siehe Abschnitt CONDITIONAL EXPRESSIONS in Ihrer Bash-Manpage.

Nebenbei bemerkt, sagt der SUSv3

Der von KornShell abgeleitete bedingte Befehl (doppelte Klammer [[]]) wurde in einem frühen Vorschlag aus der Beschreibung der Shell-Befehlssprache entfernt. Es wurden Einwände erhoben, dass das eigentliche Problem der Missbrauch des Befehls test ([) ist, und dass das Einfügen in die Shell der falsche Weg ist, um das Problem zu beheben . Stattdessen reichen eine ordnungsgemäße Dokumentation und ein neues Shell-reserviertes Wort (!) aus.

Tests, die mehrere Testoperationen erfordern, können auf Shell-Ebene ausgeführt werden, indem einzelne Aufrufe des Befehls test und der Shell-Logik anstelle von ausgeführt werden mit dem fehleranfälligen - o Flag von test .

sie müssten es so schreiben, aber test unterstützt es nicht:

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

test verwendet = für die String-Gleichheit, was noch wichtiger ist, es unterstützt keine Mustererkennung.

case/esac unterstützt den Mustervergleich gut:

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

es hat den zusätzlichen Vorteil, dass es nicht von bash abhängt, die Syntax ist portabel. Aus der Single Unix-Spezifikation The Shell Command Language:

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

grep

Vergessen Sie die Leistung, dies ist POSIX und sieht besser aus als case-Lösungen:

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

Erläuterung:

Ich habe @ markrushakoffs Antwort angepasst, um es zu einer aufrufbaren Funktion zu machen:

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" == "" ]]
}

Benutze es so:

$ 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

Hier ist eine komplexere Version, die einen bestimmten Standardwert vorsieht:

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* ]]
}

Benutze es so:

$ 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