![フォーク2本](https://rvso.com/image/112013/%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AF2%E6%9C%AC.png)
すべての要素をループせずに配列全体 ([キー] = 値) を印刷する方法はありますか?
いくつかの要素を持つ配列を作成したと仮定します。
declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)
配列全体を印刷するには
for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done
${!array[@]}
ただし、bash は、キーと値の両方を含むすべての配列要素を 1 回で取得する方法をすでに知っているようです${array[@]}
。
ループなしで bash にこの情報を印刷させる方法はありますか?
編集:
typeset -p array
そうなります!
ただし、1 回の置換でプレフィックスとサフィックスの両方を削除することはできません。
a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"
出力のキー=値の部分のみを取得/印刷するよりクリーンな方法はありますか?
答え1
あなたは2つの異なることを尋ねていると思います。
ループなしで bash にこの情報を印刷させる方法はありますか?
はい、しかしループだけを使用するほど良くはありません。
出力のキー=値の部分のみを取得/印刷するよりクリーンな方法はありますか?
はい、for
ループです。ループには、外部プログラムを必要とせず、簡単で、予期せず正確な出力形式を制御するのがかなり簡単であるという利点があります。
declare -p
( )の出力を処理しようとするソリューションは、typeset -p
a) 変数自体に括弧または大括弧が含まれる可能性、b) 出力をシェルの有効な入力にするために追加する必要がある引用符、に対処するdeclare -p
必要があります。
たとえば、b="${a##*(}"
キー/値に開き括弧が含まれている場合、展開によって値の一部が失われます。これは##
、最長プレフィックス。 についても同様ですc="${b%% )*}"
。 もちろん、 によって印刷される定型句とdeclare
より正確に一致させることはできますが、 が行う引用符付けをすべて必要としない場合は、やはり困難が伴います。
必要でない限り、見た目はあまり良くありません。
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
ループを使用するとfor
、出力形式を好みに応じて簡単に選択できます。
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
そこからは、単純それ以外の場合は、出力形式を変更します (キーを囲む括弧を削除し、すべてのキー/値のペアを 1 行にまとめます...)。シェル自体以外のものに引用符が必要な場合は、自分で行う必要がありますが、少なくとも作業する生のデータは手に入ります。(キーまたは値に改行がある場合は、おそらく引用符が必要になります。)
現在の Bash (4.4 だと思います) では、printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
の代わりにを使用することもできますprintf "%q=%q"
。これにより、引用符で囲まれた形式がいくらか見栄えが良くなりますが、もちろん、書くことを覚えておくための作業が少し増えます。(また、 のコーナーケースを@
配列キーとして引用符で囲みますが、これは%q
引用符で囲みません。)
for ループを書くのが面倒すぎる場合は、関数としてどこかに保存します (ここでは引用符を付けません)。
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
そして、それを使用します:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
インデックス付き配列でも動作します:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
答え2
declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'
フォーク2本
たぶんこれ:
printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1
printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb
printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2 2
a1 1
f50 abcd
zz Hello World
b1 bbb
フォーク3本
またはこれ:
paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
フォークなし
比較される
for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
実行時間の比較
最後の構文はフォークを使用しないため、より速くなる可能性があります。
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
5 11 76
real 0m0.005s
user 0m0.000s
sys 0m0.000s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
5 6 41
real 0m0.008s
user 0m0.000s
sys 0m0.000s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
5 6 41
real 0m0.002s
user 0m0.000s
sys 0m0.001s
しかし、配列が大きくなると、この断言は当てはまらなくなります。小さなプロセスではフォークを減らすことが効率的ですが、大きなプロセスでは専用ツールを使用する方が効率的です。
for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
17581 35163 292941
real 0m0.150s
user 0m0.124s
sys 0m0.036s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
17581 17582 169875
real 0m0.140s
user 0m0.000s
sys 0m0.004s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
17581 17582 169875
real 0m0.312s
user 0m0.268s
sys 0m0.076s
述べる
両方として (分岐した) ソリューションの使用配置、変数に改行この場合、唯一の方法はfor
ループです。
StackOverflowでより強力で詳細な回答
答え3
K
Bash 5.1では、次のように値を使用して連想配列を表示する非常に簡単な方法が提供されています${arr[@]@K}
。
$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2"
hh. 連想配列をキーと値のペアとして表示するための新しい「K」パラメータ変換。
それは、Bash リファレンスマニュアル → 3.5.3 シェルパラメータの展開:
${parameter@operator}
け
パラメータの値の引用符で囲まれた可能性のあるバージョンを生成します。ただし、インデックス付き配列と連想配列の値は引用符で囲まれたキーと値のペアのシーケンスとして出力されます (配列を参照)。
答え4
以来タイプセット目的の動作をするには、出力を編集するだけでよいのではないでしょうか?
typeset -p array | sed s/^.*\(// | tr -d ")\'\"" | tr "[" "\n" | sed s/]=/' = '/
与える
a2 = 2
a1 = 1
b1 = bbb
どこ
array='([a2]="2" [a1]="1" [b1]="bbb" )'
冗長ではありますが、フォーマットがどのように機能するかは簡単にわかります。パイプラインを徐々に実行して、sedそしてtrコマンド。きれいな印刷の好みに合わせて変更します。