web-dev-qa-db-ja.com

whileループの後で配列が空になるのはなぜですか?

次の方法でbash-4.2で配列を初期化しようとします。

ring=()
ls -las | tail -n +4 | while read line
> do
> ring+=("$line")
> echo ${ring[-1]}
> done
3924 -rw-r--r-- 1 username group 4015716 Mar 23 15:14 script.jar
4 -rw-r--r-- 1 username group 9 Feb 29 12:40 rec.lst
5541 -rw-r--r-- 1 username group 5674226917 Mar 28 15:25 debug.out
8 -rw-r--r-- 1 username group 6135 Mar 25 12:16 script.class
8 -rw-r--r-- 1 username group 6377 Mar 25 11:57 script.Java
8 -rwxr-xr-x 1 username group 4930 Mar 8 15:21 script-0.0.0.sh
8 -rwxr-xr-x 1 username group 6361 Mar 28 15:27 script-0.0.1.sh
echo ${ring[0]}

echo "${ring[0]}"

echo "${ring[@]}"

何が問題なのですか、ループの終了後に空の配列が表示されるのはなぜですか?

3
Dimaf

あなたの問題は、パイプライン(_command1 | command2 | command3 ..._)でコマンドがサブシェルで実行されることです。変数は、サブシェル間、またはサブシェルとメインシェル間で共有されません。 whileループのringは、メインシェルのringとは異なります。

これを克服する1つの方法は、プロセス置換を使用することです。

_while read line;  do  ring+=("$line");  echo ${ring[-1]};  done < <(ls -las|tail -n +4) 
_

<(command)構文はプロセス置換と呼ばれ、コマンドの出力を名前付きパイプにリダイレクトします。次に、ファイルと同じように、おなじみの_<_でリダイレクトされます。 _<_を使用すると、サブシェルがないため、ring変数が設定されます。

ファイルの行から配列を埋めるShell組み込みコマンドがあることに注意してください。

_mapfile -t ring < <(ls -las | tail -n +4)
_
8
RealSkeptic

これはうまくいくはずです:

ring=()
while read line
do
ring+=("$line")
echo ${ring[-1]}
done < <(ls -las | tail -n +4)

出典: http://wiki.bash-hackers.org/syntax/arrays

2
Paweł Miązek

その理由は、whileループがパイプされるという事実により、サブシェルで実行されるためです。サブシェルは親シェル環境のコピーを使用し、サブシェルが終了するときにそれを返しません。

bashの場合は、コマンドグループ化の回避策を使用できます。中括弧が追加されていることに注意してください

ls -las | tail -n +4 | { while read line;  do  ring+=("$line");  echo ${ring[-1]};  done;  echo ${ring[0]}; }
0
Tagwint