フォーク2本

フォーク2本

すべての要素をループせずに配列全体 ([キー] = 値) を印刷する方法はありますか?

いくつかの要素を持つ配列を作成したと仮定します。

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 -pa) 変数自体に括弧または大括弧が含まれる可能性、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

KBash 5.1では、次のように値を使用して連想配列を表示する非常に簡単な方法が提供されています${arr[@]@K}

$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2" 

からBash 5.1 の説明文書:

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コマンド。きれいな印刷の好みに合わせて変更します。

関連情報