Bash 組み込みの「読み取り」がコメントや空白行を無視するようにするにはどうすればよいですか?

Bash 組み込みの「読み取り」がコメントや空白行を無視するようにするにはどうすればよいですか?

(単純化のために読み取るファイルが最初の引数であると仮定します。$1

私は私が望むことをすることができます外部的にそして:

tempfile=$(mktemp)
awk '/^#/ {next}; NF == 0 {next}; {print}' "$1" > $tempfile
while read var1 var2 var3 var4 < $tempfile; do
  # stuff with var1, etc.
done

awkしかし、設定ファイルを解析するたびにそれを呼び出さなければならないということはとんでもないようです。readファイルのコメント行または空白行を無視する方法はありますか?いいえ外部バイナリ/潜在的なパフォーマンスの問題はありますか?


これまでの答えはとても役に立ちます!はっきり言えば一時ファイルを使いたくないけどする構成を読みたいです。ファイルから、標準入力ではありません。スクリプトを呼び出すときに入力リダイレクトを使用できることはよくわかっていますが、いくつかの理由で私の場合、これはうまくいきません。

読みたい入力をソフトエンコードしたいです。たとえば、次のようになります。

configfile="/opt/myconfigfile.txt"
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

while read var1 var2 var3 var4 < "$configfile" ; do
  ...

configfileただし、これを試みると、プロセスが終了するまで最初の行を読み続けます。

たぶんこれはそれ自体の質問でなければならないかもしれませんが…私がやっていることから行が変わるかもしれません。私の間違いはどこにありますか?

答え1

これを行うために一時ファイルは必要なく、sed(またはawk)はシェルケースステートメントよりもコメントアウトにはるかに柔軟です。

たとえば、

configfile='/opt/myconfigfile.txt'
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile" |
    while read var1 var2 var3 var4; do
      # stuff with var1, etc.
    done

# Note: var1 etc are not available to the script at this
# point. They are only available in the sub-shell running
# the while loop, and go away when that sub-shell ends.

これにより、コメント(先行スペースを含めるか除く)が削除され、whileループに入力を供給する前に入力から空白行が削除されます。その行のコメントと行の末尾に追加されたコメントを別々に処理します。

# full-line comment
# var1 var2 var3 var4
abc 123 xyz def # comment here

このような電話やsed仕事はawk「愚かな」仕事ではなく、完全に正常なものです。これがこのツールの目的です。パフォーマンスに関しては、非常に小さな入力ファイルを除いて、このバージョンははるかに高速になると確信sedしています。配管にはsed少しの開始オーバーヘッドがありますが、非常に高速に実行されますが、シェルは遅いです。


2022年5月3日に更新:

while ループ (var1、var2、var3 など) に設定された変数は、while ループが終了すると「範囲外」です。 whileループ内でのみ使用できます。 whileループは、構成ファイルがパイプでリンクされているため、サブシェルで実行されます。サブシェルが死ぬと、その環境とサブプロセスも一緒に消えます。できない親プロセスの環境を変更します。

whileループ後も変数の値が保持されるようにするには、パイプの使用を避ける必要があります。たとえば、入力リダイレクト(<)を使用します。プロセスの交換( <(...)):

while read var1 var2 var3 var4; do
  # stuff with var1, etc.
done < <(sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile")

# remainder of script can use var1 etc if and as needed.

このプロセス代替バージョンでは、whileループは親シェルで実行され、スクリプトsedは子プロセスとして実行されます(その出力はwhileループにリダイレクトされます)。 sedとその環境は完了すると消えますが、whileループを実行するシェルはループによって生成/変更された変数を保持します。

答え2

これは、スペース(IFS)のすべてのエントリが削除されるために機能しますread。したがって、var1が空の場合、または「#」で始まる場合はスキップされます。

while read var1 var2 var3 var4; do
   case $var1 in
       ''|\#*) continue ;;         # skip blank lines and lines starting with #
   esac
   echo "var1: '$var1'"
   # stuff with var1, etc.
done < "${1:-default_config_file}"

whileその後、入力をコマンドリストの代わりにループにリダイレクトする必要があります。空でない場合は"${1:-default_config_file}"最初のコマンドライン引数に展開され、それ以外の場合は次に展開されますdefault_config_file。デフォルト値の文字列などで変数拡張を使用することもできます。

最小限の前処理に興味があるので、これは同じだと思いますが、すべてのコメントも削除します。

while read line; do
    echo "${line%%#*}" | {
        read var1 var2 var3 var4
        [ -z "$var1" ] && continue
        # stuff with var1, etc.
        for i in 1 2 3 4; do eval echo "\"var$i: \$var$i\""; done  #debug only!
    }
done < "${1:-default_config_file}"

これはシェルパラメータ拡張サブストリング処理機能を使用します。削除後、最初の値とすべての項目を除いて元の値に展開されます${line%%#*}。ロードし、通常どおり続けます。代わりに空の文字列をチェックするだけで、テストが短縮されます。line#var1-4continue#

答え3

一時ファイルを作成せずにこれを行うことができます。 grep コマンドは、空行とコメント行をフィルタリングします。

while read var1 var2 var3; do
    echo $var1
    echo $var2
    echo $var3
    echo "etc..."
done < <(grep -v "^#\|^$" /opt/myconfigfile.txt)

関連情報