POSIXと移植性grep -s、grep -q |

POSIXと移植性grep -s、grep -q |

私はシェルスクリプトの移植性を好みます。

しかし、今は行き過ぎたのか分かりません。

この例では、confirmation最初の引数を質問を含む文字列として受け入れ、他のすべての引数には有効な答えを持つ関数があります。


confirmation ()
{
    question=$1; shift; correct_answers=$*

    printf '%b' "$question\\nPlease answer [ $( printf '%s' "$correct_answers" | tr ' ' / ) ] to confirm (Not <Enter>): "; read -r user_answer

    # this part iterates through the list of correct answers
    # and compares each as the whole word (actually as the whole line) with the user answer
    for single_correct_answer in $correct_answers; do
        printf '%s' "$single_correct_answer" | grep -i -x "$user_answer" > /dev/null 2>&1 && return 0
    done

    return 1
}

confirmation 'Do you hate me?' yes yeah kinda

ご覧のとおり、コア部分がExploitingされてgrep見てきました。マニュアルページ、そしてこれを見つけました:

-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. (-q is specified by POSIX .)
-s, --no-messages
Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX , because it lacked -q and its -s option behaved like GNU grep's -q option. USG -style grep also lacked -q but its -s option behaved like GNU grep. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX .)

この部分を強調してみましょう。

移植性に関する注意:GNUとは異なり、grepUnixの7番目のバージョンgrepPOSIX不足しており、-qオプションがGNUのオプション-sと同様に機能するためです。 USGにも特徴がありませんが、オプションはGNUと同様に機能します。移植可能なシェルスクリプトは、使用を避ける必要があり、標準出力とエラー出力を 。grep-qgrep-q-sgrep-q-s/dev/null


遠くに行きましたか?それとも/dev/null唯一のポータブルな方法でリダイレクトしていますか?


いいえ40年前、オペレーティングシステムのバージョンの移植性に集中してください!

答え1

Unix V7は1970年代後半に発売された。これはBourneシェルを導入したバージョンです。

しかし、当時追加された機能サポートはなく、readno-rprintfコマンドもありませんでした。大文字と小文字を区別しないのはBourneではgrepありません。grep -y$(...)

それ以来、Unixシリーズシステムは多くの進歩と多様化してきました。 1990年代初頭、POSIXはある程度統一性を回復しようとしました。

ただし、基本的な実装では、POSIXに準拠せず、POSIX準拠の実装のみを別のユーティリティとして追加するいくつかのシステムがまだあります。

たとえば、SolarisはPOSIXより/bin/grepV7に近いです。 Solaris の POSIX を使用できます (Solaris の最小展開では使用できません)。grepgrepgrep/usr/xpg4/bin/grep

/bin/grep-qソラリスには、、-Eがありません-F

/usr/xpg4/binSolarisで作業するときは、通常、前に置いて代わりに$PATH使用しようとしています(Solaris 11では変更されましたが、/ bin / shは現在ksh93なので、バグベースのksh88よりも平均的にPOSIX互換性が高くなります)。/usr/xpg4/bin/sh/bin/sh/usr/xpg4/bin/sh

コードに関するその他の移植性に関する注意事項:

  • correct_answers=$*または の動作はread -rの現在値によって異なります$IFS。 (位置パラメータは入力を単語に分割するために使用される最初の$*文字に関連付けられています。)したがって、必要な値に設定する必要があります。$IFSread$IFS

    位置引数をスカラー文字列に連結すると、区切り文字が含まれている場合、NLを区切り文字として使用しないと正しく機能しません。readとにかく一行だけ読んで答えが含まれていないからです。改行文字。

  • シーケンスを拡張する%b意図ではない可能性がある内容を-formattedパラメーターに含めました。\x

  • for single_correct_answer in $correct_answersスプリット+グローブを使用してください。私はここでグローバル部分を望んでいないと思います。

  • grep -i -x "$user_answer"大文字と小文字を区別せずに比較する代わりに、正規表現パターンの一致を実行します。また、回答がで始まる場合はオプションなので-動作しません。grep

  • printf '%s' textテキスト以外の出力(改行不足)を生成するため、動作は指定されていませんgrep(実際には移植できません)。

したがって、これらの点を念頭に置いて、コードを次のように変更します。

confirmation()  {
    question=$1; shift

    printf '%s\nPlease answer [' "$question"
    sep=
    for answer do
      printf %s "$sep$answer"
      sep=/
    done
    printf '] to confirm (Not <Enter>): '
    IFS= read -r user_answer

    # this part iterates through the list of correct answers
    # and compares each as the whole word (actually as the whole line)
    # with the user answer
    for single_correct_answer do
      printf '%s\n' "$single_correct_answer" |
        grep -ixFqe "$user_answer" && return
    done

    return 1
}

関連情報