Bashスクリプトでパイプされたstdinと位置引数を処理するための一般的な関数

Bashスクリプトでパイプされたstdinと位置引数を処理するための一般的な関数

私のスクリプトは次のように動作します。

./script file1 file2 file3 ... -x -st -i

または

cat filelist.txt | ./script -x -st -i

または

./script <<< "$(cat filelist) -x -st -i

ファイルが場所パラメータまたはパイプまたはリダイレクトによって提供されている場合は、ファイル名に配列を設定する一般的な機能について誰かを助けることができますか?

単一のシェルスクリプトで上記のすべての状況をどのように処理できますか?

試験を終えた:

input="$(</dev/stdin)"
Arr_tmp+=("$@")
if ! echo "${Arr_tmp[@]}" | grep 'file_regex'; then  Array+=( $(echo "$input") ); else Array+=("$(echo "$@" | grep 'file_regex')"); fi

または


input="$(</dev/stdin)"
IFS=$'\n' read -ra Array -d '' <<< "$(echo $input)"
if [[ "${#Array[@]}" == 0 ]]; then 
Arr_tmp+=("$@")
fi

しかし、何も動作しません

上記の問題は、標準入力がパイプされていないと、読み込み中にスクリプトが機能しないことです。標準入力がパイプされていない場合、スクリプトがパラメータを読み取ってそのパラメータに戻るのを防ぐ方法はありますか?

ベストアンサー1

まず、オプションではなく引数の後にオプションを期待するのは悪い習慣です。

標準のコマンドラインインタフェースは、オプションではなく引数の前にオプションを置き、次に始まるオプションではなく引数を受け入れる--ことができるように、オプションの最後にオプションを表示することを許可します-

myscript -c -ofile.out -- -file1-.in -file2-.in

それは次のとおりです。

myscript -co file.out -- -file1-.in -file2-.in

たとえば、-o引数のあるオプションと-c引数のないオプションは--オプションの終わりを表示しますが、オプションでは-file1-.inなく2つの引数で、引数で始まってもオプションの終わりの後に来るため、それでも処理され-file2-.inます。議論。 。---

組み込みの標準getoptsシェルを使用して、標準ルールに従うオプションを処理できます。

getoptLinuxでは、標準オプションを処理するためにユーティリティを使用することも、util-linuxGNUスタイルの長いオプションまたはオプションの引数を持つオプションを使用することも、オプションではなく引数の後にオプションを使用できるようにすることもできます(環境を除く)$POSIXLY_CORRECT--まだ尊敬されています。getoptオプションではなく、オプションの前にオプションがある引数を適切に並べ替える操作を処理します。

コマンドラインまたは標準入力で提供された入力を処理するには、次の手順を実行する必要があります。

while getopts...; do
  # process options
  ...
done
# option processing done.
shift "$((OPTIND - 1))"
# now the positional parameters contain non-option arguments

if (( $# )); then
  args=("$@")
else
  # no non-option arguments, read them one per line from stdin
  readarray -t args
fi

それは((...))一種のクシズム(やはりサポートされているbash)、readarray一種のバシズムである。では、sh次の操作を行います。

if [ "$#" -eq 0 ]; then
  # no non-option arguments, read them from stdin
  IFS='
' # split on newline
  set -o noglob
  set -- $(cat) # read and split+glob with glob disabled
fi

結果は、上記のbash例のように、配列の代わりに位置引数($1$2...)に格納されます。この場合、空行は削除されます。"$@"$args

さらに、ファイルパスにはゼロ以外のすべてのバイト値を含めることができ、改行文字を含めることができるため、1行に任意のファイルパスのリストを渡すことはできません。

上記では、コマンドラインにオプション以外の引数が渡されていない場合にのみ標準入力で準備します。これがほとんどのテキストユーティリティが実行する操作です。たとえば、grep -e regexp -- file1 file2regexpはこれらのファイルから検索されますが、grep -e regexpstdinでは検索されません。

パラメータまたは標準入力を介して入力を提供するには、次の手順を実行する必要があります。いつも標準入力から読む:

readarray -t args
args+=("$@")

2つをにまとめます$args

ユーザーが標準入力を介して何も渡したくない場合は、いつでも次のことができます。

myscript arg1 arg2 < /dev/null

stdinが端末の場合、リストを対話形式で渡すには、ファイルのリストを入力してCtrl+で終わりますd

次のことをしたい場合があります。

if [ -t 0 ]; then
  args=()
else
  readarray -t args
fi
args+=("$@")

tty デバイスでない場合にのみ stdin を読み込みます。しかし、これはユーザーがスクリプトを無視するのが簡単であることは悪い考えだと思います。可能/dev/nullstdin以外のファイルにリダイレクトされる場合は、stdinを使用してくださいwhile IFS= read -r...; do myscript ...; done < some-file

@ilkkachuが提案したように、標準入力またはランダムファイルから入力を取得するオプションを追加して、ユーザーが標準入力を読み取るタイミングと読み取らない時期を決定することもできます。

xargs別のオプションは、入力ストリームをパラメータリストに変換するツールを使用することです。たとえば、GNUを使用すると、次のようになりますxargs

xargs -d '\n' -a filelist.txt myscript -x -st -i file1 file2

myscriptと各行の内容をパラメータとして使用file1して呼び出されます。ただし、リストがぐるぐると複数の呼び出しに分割でき、各呼び出しは内の行のサブセットを取得しますが、各呼び出しは合計も取得します。file2filelist.txtmyscriptfilelist.txtfile1file2

おすすめ記事