次の方法で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[@]}"
何が間違っていて、ループが終了した後に空の配列が表示されるのですか?
答え1
問題は、パイプ(command1 | command2 | command3 ...
)でコマンドがサブシェルで実行されることです。変数はサブシェル間やサブシェルとメインシェル間で共有されません。 while ループ内のループは、ring
基本シェルのループとは異なります。ring
この問題を克服する1つの方法は、プロセス置換を使用することです。
while read line; do ring+=("$line"); echo ${ring[-1]}; done < <(ls -las|tail -n +4)
文法<(command)
と呼ばれますプロセスの交換コマンドの出力を名前付きパイプにリダイレクトします。その後、<
まるでファイルのように、おなじみのファイルにリダイレクトされます。を使用すると<
サブシェルがないため、ring
この変数が設定されます。
ファイルの行から配列を埋めるシェルが組み込まれています。
mapfile -t ring < <(ls -las | tail -n +4)
答え2
これはうまくいきます:
ring=()
while read line
do
ring+=("$line")
echo ${ring[-1]}
done < <(ls -las | tail -n +4)
答え3
その理由は、whileループがパイプで接続されているため、サブシェルで実行されるためです。サブシェルは親シェル環境のコピーを使用し、サブシェルが終了したときにそれを再度渡しません。
Bashの場合は、コマンドのグループ化の解決策を使用できます。追加された中括弧に注意してください。
ls -las | tail -n +4 | { while read line; do ring+=("$line"); echo ${ring[-1]}; done; echo ${ring[0]}; }