配列(その3) - Bash Advent Calendar - Day 3

すでに3日めにして当初の勢いは全くなし。このままずっと続く気がしません。

閑話休題、変数展開との組み合わせで面白いことできるんじゃないの的なお話。

変数展開

普通の変数展開については既に知ってる前提で。で、配列変数においても変数展開が可能です。

まずは特定要素の変数展開をば。
$ foo=("bar" "baz")
$ echo "${foo[0]:-qux}"
bar
$ echo "${foo[2]:-qux}"
qux
と期待通りの動作をしてくれます。デフォルト値の設定も同様に
$ foo=("bar" "baz")
$ : "${foo[2]:=qux}"
$ declaare -p foo
declare -a foo='([0]="bar" [1]="baz" [2]="qux")'
$ : "${foo[1]:=quux}"
$ declaare -p foo
declare -a foo='([0]="bar" [1]="baz" [2]="qux")'
と期待通りの動作ですね。

その他にも ${var:?word} ${var:+word} も同様に想像通りの動作をしてくれます。

応用編

特定要素ではなくインデックスに @ を指定して変数展開をすると、文字列処理が一気に出来ます。Python ユーザーあたりにはウケがよさそうな
$ foo=("bar" "baz")
$ qux=("${foo[@]//a/A}")
$ declare -p qux
declare -a qux='([0]="bAr" [1]="bAz")'
みたいなことができてしまいます。これは配列の各々の要素において "a" を "A" に置換しています。このように for ループ無しで一気に文字列を処理できます。

これって Python での
>>> foo = ['bar', 'baz']
>>> qux = [s.replace('a', 'A') for s in foo]
>>> qux
['bAr', 'bAz']
にちょっと似てると思いません?でもってかなり短く書けます。

当然、変数展開の範囲内でしかできませんが、もうちょっとスクリプトっぽくすると
$ backups=(*~)
$ original_files=("${backups[@]:0:-1}")
$ for f in "${original_files}"; do
>   if [[ -f "${f}" ]]; then
>     rm "${f}"~
>   fi
> done
みたいな、Pythonでいえば
>>> backups = glob.glob('*~')
>>> original_files = [f[0:-1] for f in backups]
>>> for f in original_files:
...   if os.path.exists(f):
...     os.unlink(f + '~')
...
感じのことができます(ただし Bash スクリプトの例では、ファイル名に空白が含まれていると期待した動作をしません)。ちゃんと説明すると、
  1. *~ でファイル名が ~ で終わるファイルのリストを取得
  2. それらのファイル名から末尾の ~ を削除
  3. ~ 無しのファイルが存在する場合は
    1. ~ 付きのファイルを削除
という処理をしています。

他の ${var##word}, ${var%%word} といった文字列展開と組み合わせると、さらにいろんな事ができるのですが、もう眠いので今日はこのへんで。



コメントを投稿

このブログの人気の投稿

シェル操作課題への回答

連想配列 - Bash Advent Calendar - Day 5

今、永福町のピザ戦争が熱い!