私はこのスレッドで許可されている答えに取り組んでいます。 パイプまたはコマンドライン引数からファイル名を読み取るBashスクリプト?
以下のスクリプトを使用すると、efetch(ftp://ftp.ncbi.nlm.nih.gov/entrez/entrezdirect/edirect.zip)はid(パラメータ、例えば941241313)を受け入れますが、stdinは受け入れません。
if [ $# -gt 0 ] ;then
for name in "$@"; do
efetch -db nucleotide -id $name -format gpc > $name.xml;
done
else
IFS=$'\n' read -d '' -r -a filenames
while read name; do
efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "${filenames[@]}"
fi
次のバージョンに変更すると、efetchはidの代わりにstdinを受け入れます。
if [ $# -gt 0 ] ;then
while read name; do
efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "$@"
else
IFS=$'\n' read -d '' -r -a filenames
while read name; do
efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "${filenames[@]}"
fi
何が問題なの?
ベストアンサー1
このブロックは何が変わりましたか?
while read name; do
efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "$@"
これにより、efetch
inループは引数によって提供されたファイルにリダイレクトされた標準入力として実行されます。したがって、efetch
使用方法は2つの変更されます。
- 標準入力はもはやデフォルト(ターミナル)ではありません。
- これらのパラメータのリストは、リテラルスクリプトのコマンドラインパラメータではなく、ファイルから間接的に提供されます。
入力が端末ではないことを検出すると、efetch
端末を再度開く可能性が高くなります(おそらく「efetchがidの代わりにstdinを許可します」という意味になります)。あるいは、efetch
標準入力を読んでいる場合は、予期しない内容を読むことができます(クイックテストではスクリプト自体のように見えます)。
@chepnaシェル(この場合bash)は、ループの子プロセスを作成しないことを指摘しています。私は別の状況を考えた。次の2つのスクリプトを考えてみましょう。
#!/bin/bash
LAST=...
while read name
do
/bin/echo "** $name"
LAST="$name"
done < "$@"
echo "...$LAST"
そして
#!/bin/bash
LAST=...
cat "$@" | while read name
do
/bin/echo "** $name"
LAST="$name"
done
echo "...$LAST"
後者(パイプライン)は最後に「...」をエコーしますが、前者(リダイレクト)はLAST
ループ内に割り当てられた最後の変数をエコーします。パイプを使用するフォームは、変数の割り当てがループの外に伝播されない理由を説明するために子プロセスが必要であると説明されています。
興味深いことに、後者(パイプライン)シェルには使用されるプロセスの数に違いがあります。strace -fo
システムコールとプロセスIDをキャプチャするために(Debian / test)bash、dash(/ bin / sh)、zsh、およびksh93でテストされました。
#!/bin/sh
for sh in bash dash zsh ksh93
do
echo "++ $sh"
strace -fo $sh.log ./do-$sh ./once
LC=$(sed -e 's/ .*//' $sh.log |sort -u |wc -l)
WC=$(wc -l $sh.log)
echo "-- $LC / $WC"
done
このスクリプトは、シェルあたりのプロセス数とシステムコールの数を表示します。 (ファイルには、once
テスト境界を削除するための「最初」と「2番目」の2行が含まれています。)
zshとksh93はbashとdashよりも1つ少ないプロセスを使用していることがわかりました。
$ ./testit
++ bash
** first
** second
......
-- 5 / 401 bash.log
++ dash
** first
** second
......
-- 5 / 222 dash.log
++ zsh
** first
** second
...second
-- 4 / 568 zsh.log
++ ksh93
** first
** second
...second
-- 4 / 336 ksh93.log
この例でパイプラインを実行するには、ここで説明されているプロセスよりも1つまたは2つ以上のプロセスが必要です。