一重引用符内の正規表現は値が失われますか?

一重引用符内の正規表現は値が失われますか?

私が読んでいる本、O'Reilly の Learning the Bash Shell では、次のようなコードが指定されています。

if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then 

   howmany=$1

   shift
   ....
   ....
   etc

これは、検索ユーティリティを使用して、適切なパターンに一致するgrepかどうかをテストします。これを行うには、grep に正規表現を提供します。これは、「最初のダッシュの後に数字が続き、オプションで 1 つ以上の数字が続く」と解釈されます。一致が見つかった場合は、一致が返され、テストは true になります。一致がない場合は、何も返されず、処理はテストに渡されます。$1^-[0-9][0-9]*$grepgrepelif

シェルが $ と * を解釈しないようにするために正規表現を一重引用符で囲み、変更せずに grep に渡していることに注意してください。

では、なぜ正規表現は'^-[0-9]'一重引用符内では意味を失わないのでしょうか。通常、一重引用符内のすべては意味を失います。

助けてくれてありがとう。

答え1

他の人があなたの具体的な質問に答えていますが、私はこう付け加えておきます

if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then 

文字列が正規表現に一致するかどうかを確認する方法は、いくつかの理由から間違っています。

  1. echo任意のデータには使用できません
  2. 上記のようにパラメータ展開を引用符で囲まないままにしておくのが、$1split+glob 演算子です。
  3. grep正規表現を入力全体と照合するのではなく、入力の各行と照合します。そのため、たとえば true を返しますfoo\n-0\nbar
  4. 正規表現は長さが 0 であっても一致する可能性があるため、一般に がgrep出力を生成するかどうかを確認するのは誤りです (コマンド置換によって末尾の改行文字が削除されることに注意してください)。 の終了ステータスではなく のgrep -q終了ステータスを使用してそれに依存し、コマンド置換も回避する方が適切です。grep[
  5. このgrepコマンドは次のように簡略化できることに注意してください。grep -xE '-[0-9]+'

bashksh93およびzsh(拡張)正規表現マッチング専用の演算子があります。これを 3 つすべて(および bash-3.1)で移植可能かつ確実に使用するには、構文は次のようになります。

re='^-[0-9]+$'
if [[ $1 =~ $re ]]; then
  echo matches
fi

yashまた、zsh以下もサポートします:

if [ "$1" '=~' '^-[0-9]+$' ]; then
  echo matches
fi

文字列(基本)正規表現マッチングを実行するための標準コマンドは次のとおりですexpr

if expr " $1" : ' -[0-9]\{1,\}$' > /dev/null; then
  echo matches
fi

^( ではなく$)は に暗黙的に含まれていることに注意してください。 の値が演算子である場合exprに問題が発生するのを避けるために、先頭のスペース文字も使用します。$1expr

また、正規表現に が含まれている場合\(...\)、 の動作に影響することに注意してくださいexpr

全体として、awk代わりに別の標準的/移植可能な方法を使用する方が適切です (awk拡張正規表現を使用することに注意してください)。

if STRING=$1 RE='^-[0-9]+$' awk '
  BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'; then
...

または関数を使用します:

re_match() {
  STRING=$1 RE=$2 awk '
    BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'
}

if re_match "$1" '^-[0-9]+$'

この場合、標準的なcase構造を使用して実現することもできます。

case $1 in
  ("" | *[!0-9-]* | [!-]* | - | ?*-*) ;;
  (*) echo match;;
esac

を使用するにはgrep、オプション(標準オプションではないためサポートされている場合)を使用して、--null改行で区切られたレコードではなく NUL で区切られたレコードで動作するように指示します。ほとんどのシェルでは、 に$1NUL を含めることはできないため、これは安全なはずです。

 if printf %s "$1" | grep --null -xq '-[0-9]+$'; then
   echo match
 fi

答え2

シングルクォートは、囲み文字をそのまま保持するようにシェルに指示します。解釈なしでgrep引用符で囲まれた文字列は引用符なしでそのままに に渡されます。grepが引数を探すとき、

grep

そして

^-[0-9][0-9]*$

そしてそれに基づいて行動するのです。(読むプログラムの実行方法Linux での引数の構築について興味がある場合。

bashgrepは異なります。このコマンドが引用符を使用する方法により、 はbash文字列を処理しませんが、grepは処理します。

答え3

シングルクォートはグロビングbashのようなワイルドカードを解釈させ*、 を使用することで変数を拡張します。基本的には、「それらの文字を文字通りに受け取り、 に渡す」$と言っていることになります。 がそれらを見ると、正規表現を理解するように構築されているので、bashgrepgrepそれから正規表現は内部で解釈されますgrep

短縮バージョン: 引数を一重引用符で囲むと、引数がコマンドに渡される前にシェルからの処理をエスケープする手段が提供されます。

答え4

意味は失われます。bashgrepとほぼ同じ正規表現パターンを使用します。

関連情報