複数行の文字列を1行ずつ繰り返します。

複数行の文字列を1行ずつ繰り返します。

/bin/shBSDプラットフォームのPOSIXシェル()で複数行の文字列を処理し、1行ずつ繰り返したいと思います。 BashはデフォルトのBSDディストリビューションには含まれておらず、GPLに基づいてライセンスされているため、一般的に使用できるように努めています/bin/sh

私はパイプを使って解決策を見つけましたが、通常の/bin/shシェルではこれらの解決策は別々のプロセスとして扱われるので、これはうまくいきません。

MULTILINE="`cat ${SOMEFILE}`"
SOMEVAR="original value"

echo "${MULTILINE}" | while IFS= read -r SINGLELINE
do
 SOMEVAR="updated value"
 echo "this is a single line: ${SINGLELINE}"
 echo "SOMEVAR is now: ${SOMEVAR}"
done

echo "Final SOMEVAR is unchanged: ${SOMEVAR}"

${SOMEVAR}上記の例では、変数の変更(つまり、whileループの外部からアクセスできない)を除いて、私が望むことを行います。

私の質問:この制限なしに同様のことをどのように実行できますか?多くのソリューションにはBashが必要ですが、私は標準のPOSIXシェルを使用しています/bin/sh

答え1

ここではマニュアルを使用できます。

while IFS= read -r SINGLELINE
do
  SOMEVAR="updated value"
  printf '%s\n' "this is a single line: ${SINGLELINE}"
  printf '%s\n' "SOMEVAR is now: ${SOMEVAR}"
done << EOF
$MULTILINE
EOF
printf '%s\n' "Final SOMEVAL is still $SOMEVAR"

sh実装に応じて、ここのドキュメントは削除された一時ファイル(シェルが変数の拡張と改行文字を事前に保存する)、またはシェルが変数の拡張とそれに続くパイプを提供するパイプとして実装されます。改行。どちらの場合も、リダイレクトされたコマンドは元のBourneシェル(現在は廃止され、POSIX準拠のシェルではない)以外のサブシェル(POSIXで要求されているように)では実行されません。

または分割+globを使用できます。

IFS='
' # split on newline only
set -o noglob
for SINGLELINE in $MULTILINE
do
  SOMEVAR="updated value"
  printf '%s\n' "this is a single line: ${SINGLELINE}"
  printf '%s\n' "SOMEVAR is now: ${SOMEVAR}"
done
printf '%s\n' "Final SOMEVAL is still $SOMEVAR"

しかし、空行はスキップされます。

答え2

パイプなしでファイルから直接読み取ることができます。これにより、サブシェルでループが実行されるのを防ぎ、whileループ後に変更された値を表示できます。$SOMEVALUE

SOMEVAR="original value"

while IFS= read -r SINGLELINE
do
    SOMEVAR="updated value"
    printf 'this is a single line: %s\n' "$SINGLELINE"
    printf 'SOMEVAR is now: %s\n' "$SOMEVAR"
done <"$SOMEFILE"

printf 'Final SOMEVAR is: %s\n' "$SOMEVAR"

変数の使用に固執する場合は、$MULTILINEファイルに書き込み、そこから読みます。

tmpfile=$(mktemp)
printf '%s\n' "$MULTILINE" >"$tmpfile"

while ...; do
   ...
done <"$tmpfile"
rm "$tmpfile"

また関連:

$SOMEVAR上記の質問に対する回答では、すべての使用がパイプの末尾のサブシェルで発生するようにプログラムを作成することをお勧めします。

MULTILINE=$(cat "$SOMEFILE")
SOMEVAR="original value"

printf '%s\n' "$MULTILINE" | {
    while IFS= read -r SINGLELINE
    do
        SOMEVAR="updated value"
        printf 'this is a single line: %s\n' "$SINGLELINE"
        printf 'SOMEVAR is now: %s\n' "$SOMEVAR"
    done

    printf 'Final SOMEVAR is: %s\n' "$SOMEVAR"
}

また関連することができます:

興味があるかもしれない他の質問:

答え3

これは私にとって効果的です。

$ cat bin/test
#! /bin/sh
SOMEFILE=$1
MULTILINE="`cat ${SOMEFILE}`"
SOMEVAR="blah"

echo "${MULTILINE}" | while IFS= read -r SINGLELINE
do
 echo "this is a single line: ${SINGLELINE}"
 echo "but accessing this var fails: ${SOMEVAR}"
done

そして

$ bin/test bin/test
this is a single line: #! /bin/sh
but accessing this var fails: blah
this is a single line: SOMEFILE=$1
but accessing this var fails: blah
this is a single line: MULTILINE="`cat ${SOMEFILE}`"
but accessing this var fails: blah
this is a single line: SOMEVAR="blah"
but accessing this var fails: blah
this is a single line:
but accessing this var fails: blah
this is a single line: echo "${MULTILINE}" | while IFS= read -r SINGLELINE
but accessing this var fails: blah
this is a single line: do
but accessing this var fails: blah
this is a single line:  echo "this is a single line: ${SINGLELINE}"
but accessing this var fails: blah
this is a single line:  echo "but accessing this var fails: ${SOMEVAR}"
but accessing this var fails: blah
this is a single line: done
but accessing this var fails: blah

関連情報