Bashでは、文字列が何らかの値で始まるかどうかをどのように確認できますか?


745

文字列が「node」で始まるかどうかを確認したい(例:「node001」)。何かのようなもの

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

どうすれば正しくできますか?


さらに、HOSTが「user1」であるか「node」で始まるかを確認するために式を組み合わせる必要があります

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

> > > -bash: [: too many arguments

どうすれば正しくできますか?


7
式を組み合わせようとしないでください。より適切なエラーメッセージを提供し、スクリプトをデバッグしやすくすることはできますが、2つの個別の条件があると見栄えが悪くなります。また、bash機能は避けます。スイッチは進むべき道です。
ヘンドリー、2010年

回答:


1073

Advanced Bash Scripting Guideのこのスニペットは言う:

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

だからあなたはそれをほぼ正しいものにしました。単一のブラケットではなく、二重のブラケットが必要でした。


2番目の質問に関しては、次のように書くことができます。

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

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

反響します

yes1
yes2

Bashのif構文は慣れにくい(IMO)。


12
正規表現では、[[$ a =〜^ z。*]]ですか?
JStrahl 2013年

3
との間に機能的な違いは[[ $a == z* ]]あり[[ $a == "z*" ]]ますか?つまり、動作が異なりますか?そして、「$ aはz *と等しい」と言うとき、具体的にどういう意味ですか?
Niels Bom

5
文の区切り記号 ";"は必要ありません。「then」を独自の行に配置した場合
Yaza

6
完全を[[ $a == *com ]]
期す

4
ABSは不幸な参照の選択です-それは非常に古いbashのW3Schoolsであり、時代遅れのコンテンツと悪い習慣の例でいっぱいです。freenode #bashチャネルは、少なくとも2008年以降、その使用を阻止しようとしています。BashFAQ#31再度ポイントする可能性はありますか?(私はBash-Hackersのwikiも提案しましたが、今は少しダウンしています)。
Charles Duffy

207

最近のバージョンのBash(v3 +)を使用している場合は、Bash正規表現比較演算子をお勧め=~します。たとえば、

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

this or that正規表現で照合するに|は、たとえばを使用します。

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

注-これは「適切な」正規表現構文です。

  • user*use0以上のの出現を意味するrのでuseuserrrrと一致します。
  • user.*user、任意の文字の0回以上の出現を意味するためuser1userXが一致します。
  • ^user.*user.*$ HOSTの最初のパターンに一致することを意味します。

正規表現の構文に慣れていない場合は、このリソースを参照しください。


ありがとう、ブラブスター!ifで表現を組み合わせる方法についての新しい質問を元の投稿に追加しました。
Tim

2
受け入れられた答えが正規表現の構文について何も言わないのは残念です。
CarlosRos

20
FYI bash =~演算子は、右側がUNQUOTEDの場合にのみ正規表現マッチングを行います。右側を引用する場合、「パターンの任意の部分を引用符で囲んで、文字列として一致させることができます。」(1.)正規表現は常に引用符で囲まずに右に配置します。(2。)正規表現を変数に格納する場合は、パラメーター展開を行うときに右側を引用符で囲まないようにしてください。
Trevor Boyd Smith、

145

shスクリプティングの主要なポイントの1つは移植性(プログラムの接続ではなく、プログラムの置き換えではない)であるため、私は常にBash拡張機能を使用する代わりにPOSIX を使用するようにしています。

ではsh、「is-prefix」条件をチェックする簡単な方法があります。

case $HOST in node*)
    # Your code here
esac

何歳を考えると、難解とcrufty shがある(とバッシュは治療ではありません:それは、より少ない一貫性の少ないポータブル、複雑です)、私は非常に素晴らしい機能面を指摘したいと思います:いくつかのシンタックス要素が好きながらcase、内蔵されています、結果の構成は他のジョブと何の違いもありません。同じ方法で構成できます。

if case $HOST in node*) true;; *) false;; esac; then
    # Your code here
fi

またはもっと短い

if case $HOST in node*) ;; *) false;; esac; then
    # Your code here
fi

またはさらに短くする(!言語要素として提示するためだけに-これは現在悪いスタイルです)

if ! case $HOST in node*) false;; esac; then
    # Your code here
fi

明示的にしたい場合は、独自の言語要素を作成します。

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

これは実際にはかなりいいですね。

if beginswith node "$HOST"; then
    # Your code here
fi

そして、sh基本的にはジョブと文字列リスト(そしてジョブが構成される内部プロセス)だけなので、簡単な関数型プログラミングも実行できます。

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

これはエレガントです。私がsh深刻なものに使用することを提唱しているわけではありません-現実世界の要件であまりにも速く壊れます(ラムダがないため、文字列を使用する必要があります。ただし、関数呼び出しを文字列でネストすることはできません。パイプは不可能です)。


12
+1これはポータブルであるだけでなく、読みやすく、慣用的で、エレガントです(シェルスクリプト用)。また、自然に複数のパターンに拡張されます。case $HOST in user01 | node* ) ...
tripleee 2013

このタイプのコードフォーマットの名前はありますか?if case $HOST in node*) true;; *) false;; esac; then 私はそれをあちこちで見ました、私の目にはそれはちょっとくしゃくしゃに見えます。
Niels Bom

@NielsBom私はあなたがフォーマットによって正確に何を意味するのか分かりませんが、私のポイントはシェルコードが非常に構成可能であるということでした。Becaues caseコマンドはコマンドがあり、彼らは中に入ることができますif ... then
Jo So

なぜそれが合成可能であるのかさえわかりません、そのための十分なシェルスクリプトを理解していません:-)私の質問は、このコードが一致しない括弧と二重セミコロンをどのように使用するかについてです。これは以前見たシェルスクリプトのようには見えませんが、shスクリプトよりもbashスクリプトを見るのに慣れているので、そうかもしれません。
Niels Bom

注:それはする必要がありますbeginswith() { case "$2" in "$1"*) true;; *) false;; esac; }場合はそれ以外の$1リテラルを持っている*か、?それが間違った答えを与えるかもしれません。
LLFourn 2016

80

チェックしたい文字列の部分だけを選択できます:

if [ "${HOST:0:4}" = user ]

フォローアップの質問には、ORを使用できます。

if [[ "$HOST" == user1 || "$HOST" == node* ]]

8
二重引用符で囲む必要があります${HOST:0:4}
Jo So

@Jo So:理由は何ですか?
Peter Mortensen、

@PeterMortensen、お試しくださいHOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
Jo So

60

私はすでに投稿されている他の方法を好みますが、一部の人々は使用したいです:

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

編集:

上記のケースステートメントに代替案を追加しました

編集したバージョンでは、括弧が多すぎます。次のようになります。

if [[ $HOST == user1 || $HOST == node* ]];

ありがとう、デニス!ifで表現を組み合わせる方法についての新しい質問を元の投稿に追加しました。
Tim

11
「好きな人…」:これはバージョンやシェル間で移植性が高いです。
カルロサヤム'17年

case文を使用すると、単語の分割が発生しないため、変数を囲む引用符を省略できます。私はそれが無意味で一貫性がないことを知っていますが、引用を残してローカルでより視覚的に魅力的なものにするのが好きです。
Jo So

そして、私の場合、前に引用符を残さなければなりませんでした): "/ *")は機能しませんでした、/ *)は機能しました。(/で始まる文字列、つまり絶対パスを探しています)
Josiah Yoder

36

ここではほとんどの答えはかなり正しいと思いますが、それらの多くには不要なバシズムが含まれています。POSIXパラメータの拡張により、必要なものがすべて提供されます。

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

そして

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

${var#expr}一致する最小のプレフィックスを取り除きexpr${var}それを返します。したがって場合${host}ないで始まるusernode${host#user}${host#node})と同じです${host}

exprfnmatch()ワイルドカードを許可し、したがって${host#node??}、そして友人も機能します。


2
バシズム[[ $host == user* ]]は、よりも読みやすいので、必要かもしれないと私は主張し[ "${host#user}" != "${host}" ]ます。そのため、スクリプトを実行する環境を制御することが許可されている(最新バージョンのを対象とするbash)ため、前者が望ましいです。
x-yuri 2018年

2
@ x-yuri率直に言って、これをhas_prefix()関数にパックして、二度と見ないようにします。
2018年

30

#バッシュで意味があるので、私は次の解決策を得ました。

さらに、スペースなどを克服するために、文字列を ""でパックする方が好きです。

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

これはまさに私が必要としたものでした。ありがとう!
IonicăBizău

おかげで、で始まる文字列をどのように照合するかも疑問に思っていました。blah:これが答えのようです!
エントロピー2015

12

Mark Rushakoffの最高ランクの回答に構文の詳細を少し追加します。

表現

$HOST == node*

次のように書くこともできます

$HOST == "node"*

効果は同じです。ワイルドカードが引用テキストの外側にあることを確認してください。ワイルドカードが引用符の内側にある場合、それは文字どおりに解釈されます(つまり、ワイルドカードとしてではありません)。


8

@OP、両方の質問に対して、case / esacを使用できます。

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

二番目の質問

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

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

または Bash 4.0

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

;&バッシュ> = 4でのみ利用可能です
追って通知があるまで一時停止。

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

すべてのための仕事は、ない[[[test同じ非再帰文法を認識しています。Bashのmanページの「条件式」を参照してください。

余談ですが、SUSv3によると

KornShellから派生した条件付きコマンド(二重ブラケット[[]])は、初期の提案でシェルコマンド言語の説明から削除されました。実際の問題はテストコマンド([])の誤用であり、それをシェルに入れるのは問題を修正するための間違った方法であるという異論が提起されました。代わりに、適切なドキュメントと新しいシェル予約語()で十分です。

複数必要とするテストのテスト動作は、個々の呼び出し用いシェル・レベルで行うことができ、テストコマンドとシェル論理名を、よりもむしろエラープローン使用-oフラグ試験

このように書く必要がありますが、テストはそれをサポートしていません:

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

テストで=を使用して文字列が等しいことを示します。さらに重要なのは、パターンマッチングをサポートしていないことです。

case/ esacはパターンマッチングを適切にサポートしています。

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

Bashに依存しないという追加の利点があり、構文は移植可能です。以下からのシングルUnixの仕様シェルコマンド言語

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac

1
[そして、testBashの組み込みコマンドだけでなく、外部のプログラムです。お試しくださいtype -a [
追って通知があるまで一時停止。

「compound or」の問題を説明してくれて本当にありがとう。乾杯!PSノート(OPとは無関係)if [ -z $aa -or -z $bb ]:; ...「bash:[:-or:二項演算子が必要です」; しかしif [ -z "$aa" -o -z "$bb" ] ; ...合格。
sdaau 2011年

2

grep

パフォーマンスを忘れて、これはPOSIXであり、caseソリューションよりも見栄えが良いです。

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

説明:


2

@markrushakoffの答えを調整して、呼び出し可能な関数にしました。

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

次のように使用します。

$ 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

以下は、指定されたデフォルト値を提供するより複雑なバージョンです。

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

次のように使用します。

$ 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

-5

あなたができるもう一つのことはcat、あなたが反響しているものとパイプすることですinline cut -c 1-1

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.