このシェルスクリプト関数の意味は何ですか

このシェルスクリプト関数の意味は何ですか

誰かが私に、例えば各行の意味を教えてもらえますか?正規表現が使用される理由を理解していません。[!0122...]

#!/bin/sh
is_integer ()
{
    case "${1#[+-]}" in
        (*[!0123456789]*) return 1 ;;
        ('')              return 1 ;;
        (*)               return 0 ;;
    esac
}

答え1

#!/bin/sh

シェル構文にコメントがあります。ただし#!、これはファイルが実行されたときにそのパスに格納されている/bin/shインタプリタを使用してファイルを解釈する必要があり、スクリプトへのパスを引数として実行する必要があることをカーネルに伝えます。

is_integer () compound-command

関数定義のためのPOSIX sh構文。

{
   ...
}

複合コマンド~と呼ばれるコマンドグループ。唯一の目的は、コマンドをグループ化して関数の本体にすることです。ここでは内容がただ一つなので重複しています。複合コマンドしかし、{ ... }コマンドグループを各関数の本文として使用するのが一般的で、コードを読みやすくするため、一般的にお勧めします。同じ関数を次のように書くことができます。

is_integer () case "${1#[+-]}" in
  (*[!0123456789]*) return 1 ;;
  ('')              return 1 ;;
  (*)               return 0 ;;
esac

case something in (pattern1 | pattern2) ...;; (pattern3)... ; esaccase/構造ですesac複合コマンド) はsomething各パターンを順番に一致させ、最初の一致で対応するコードを実行します。

これは。something${1#[-+]}パラメータ拡張、関数の最初の引数である引数に${param#pattern}演算子を適用します。1この演算子は、引数の内容の先頭からパターンに一致する最も短い文字列を削除します。または文字に一致するワイルドカードパターンです[-+](正規表現ではありません)。したがって、符号は削除された最初の引数の値に拡張されます。したがって、最初の引数があれば2になります。その場合は空の文字列になります。それでは、滞在してみてください。-+${1#[-+]}-2-22

引用されていることがわかります"${1#[+-]}"。通常、パラメータ拡張を引用する必要があります。それ以外の場合は、分割+グローブの影響を受けます。ここではこれが起こらない非常に少数のケースの1つです。

その後、この値はいくつかのパターンと一致します。

*[!0123456789]*is *- 任意の数の文字(ほとんどのシェルは非文字も受け入れますが) - の後に - ...または - 以外の任意の[!0123456789]文字の後に任意の数の文字(再)が続きます。したがって、10進数以外の文字(またはほとんどのシェルでは非文字)を含むすべての文字列と一致します。019*

一致するものがあれば、コードが実行され、関数がゼロ以外の数値であるかのように終了コードをreturn 1返します。1間違った/失敗する

''空の文字列を表す方法です。空の文字列も有効な数値ではありませんが、前のパターンと一致しません。

だから*何でも合わせてください。したがって、return 0前のパターンと一致しない文字列に対して実行されます。caseステートメントは関数の最後のコマンドであり、caseステートメントは次を返すため、ここでは重複します。成功/本物内部に実行コマンドがない場合。

したがって、ここで関数定義は次のように短縮できます。

is_integer() case ${1#[-+]} in
  ('' | *[!0123456789]*) false
esac

それはそれほど明確ではありませんが。

とにかく、そのコードを使用するのが正しいです[0123456789]。特に、入力検証の場合(シェル算術式で入力を使用するときに入力を検証することが重要です。シェル算術評価における整理されていないデータ使用のセキュリティ影響[0-9]または[[:digit:]]する必要がありますいいえsh具体的には、実装が0から9の範囲で並べられたすべての文字(または複数文字のソート要素)と一致する可能性があり、bash一部のBSDでは、0123456789英語システムの数だけでなく、すべての10進数の数字と一致する場合に特に使用できます。英語ロケールから。[0-9][[:digit:]]

たとえば、GNUシステムの一般的なアメリカ英語ロケール(現在のUTF-8を文字セットとして使用する傾向がある)のinはbash一致[0-9]します。

答え2

それは戻ってくる本物(ゼロ)関数の最初の引数が整数の場合間違った(1) そうでない場合。

まず、最初のパラメータ値で始まる単一または記号を削除して+これを実行します。-それがまさにそのことです"${1#[+-]}"。これは${variable#pattern}、変数値の先頭から最も短い部分文字列一致を削除する標準パラメータ拡張を使用します。パターンは正規表現ではなく、シェルワイルドカードパターンでなければなりません。patternvariable

次に、一連のパターンマッチング(正規表現ではなくワイルドカードパターン)を使用して結果値を実行します。最初に一致するパターンがそのステートメントをトリガーしますreturn

最初のパターンは、文字列に数字以外の文字があるかどうかをテストします。このパターンは*[!0-9]*orで書くこともできます*[![:digit:]]*(しかし、以下も参照してください)。ここ)。この関数は、正規表現の文字クラスまたは範囲で a と同じ方法で実行されます!(つまり、 に示すように一部のシェルではここでも a を許可します)。つまり、与えられた文字クラスまたは範囲を逆に置き換えます。このパターンは、「与えられた文字列の任意の位置で数字ではないものと一致する」ことが理解され得る。シェルグローブパターンは常に固定されているため、パターンの開始と終了に必要です(対応する正規表現は、またはであり、明示的な固定は必要ありません)。^[^...]^*[!0123456789]**[^0-9]^.*[^0-9].*$

2番目のモードは、文字列が空であるかどうかをテストします。

最後のパターンはすべての文字列と一致します。

in関数の代替実装bash==insideとのパターン一致を許可する[[ ... ]]):

is_integer () {
    set -- "${1#[+-]}"

    if [ -z "$1" ] || [[ $1 == *[!0-9]* ]]; then
        return 1
    fi

    return 0
}

関連情報