Bashスクリプトのwhileループは標準入力またはパラメータを読み取ります。

Bashスクリプトのwhileループは標準入力またはパラメータを読み取ります。

私はこのスレッドで許可されている答えに取り組んでいます。 パイプまたはコマンドライン引数からファイル名を読み取る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 < "$@"

これにより、efetchinループは引数によって提供されたファイルにリダイレクトされた標準入力として実行されます。したがって、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つ以上のプロセスが必要です。

おすすめ記事