
私のスクリプトは次のように動作します。
./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
シェルを使用して、標準ルールに従うオプションを処理できます。
getopt
Linuxでは、標準オプションを処理するためにユーティリティを使用することも、util-linux
GNUスタイルの長いオプションまたはオプションの引数を持つオプションを使用することも、オプションではなく引数の後にオプションを使用できるようにすることもできます(環境を除く)$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 file2
regexpはこれらのファイルから検索されますが、grep -e regexp
stdinでは検索されません。
パラメータまたは標準入力を介して入力を提供するには、次の手順を実行する必要があります。いつも標準入力から読む:
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/null
stdin以外のファイルにリダイレクトされる場合は、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
して呼び出されます。ただし、リストがぐるぐると複数の呼び出しに分割でき、各呼び出しは内の行のサブセットを取得しますが、各呼び出しは合計も取得します。file2
filelist.txt
myscript
filelist.txt
file1
file2
答え2
パイプがないときに入力を待たないようにするには、読み込み前にテストしてください。
[ -p /dev/stdin ] && read PIPEIN
Ubuntu Bash 5でテストされました。