私はシェルスクリプトの移植性を好みます。
しかし、今は行き過ぎたのか分かりません。
この例では、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とは異なり、
grep
Unixの7番目のバージョンgrep
はPOSIX不足しており、-q
オプションがGNUのオプション-s
と同様に機能するためです。 USGにも特徴がありませんが、オプションはGNUと同様に機能します。移植可能なシェルスクリプトは、使用を避ける必要があり、標準出力とエラー出力を 。grep
-q
grep
-q
-s
grep
-q
-s
/dev/null
遠くに行きましたか?それとも/dev/null
唯一のポータブルな方法でリダイレクトしていますか?
私いいえ40年前、オペレーティングシステムのバージョンの移植性に集中してください!
答え1
Unix V7は1970年代後半に発売された。これはBourneシェルを導入したバージョンです。
しかし、当時追加された機能サポートはなく、read
no-r
とprintf
コマンドもありませんでした。大文字と小文字を区別しないのはBourneではgrep
ありません。grep -y
$(...)
それ以来、Unixシリーズシステムは多くの進歩と多様化してきました。 1990年代初頭、POSIXはある程度統一性を回復しようとしました。
ただし、基本的な実装では、POSIXに準拠せず、POSIX準拠の実装のみを別のユーティリティとして追加するいくつかのシステムがまだあります。
たとえば、SolarisはPOSIXより/bin/grep
V7に近いです。 Solaris の POSIX を使用できます (Solaris の最小展開では使用できません)。grep
grep
grep
/usr/xpg4/bin/grep
/bin/grep
-q
ソラリスには、、-E
がありません-F
。
/usr/xpg4/bin
Solarisで作業するときは、通常、前に置いて代わりに$PATH
使用しようとしています(Solaris 11では変更されましたが、/ bin / shは現在ksh93なので、バグベースのksh88よりも平均的にPOSIX互換性が高くなります)。/usr/xpg4/bin/sh
/bin/sh
/usr/xpg4/bin/sh
コードに関するその他の移植性に関する注意事項:
correct_answers=$*
または の動作はread -r
の現在値によって異なります$IFS
。 (位置パラメータは入力を単語に分割するために使用される最初の$*
文字に関連付けられています。)したがって、必要な値に設定する必要があります。$IFS
read
$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
}