備忘なブレース展開と変数展開

ブレース展開やら変数展開を時々忘れるうえに意味も取り違えてたりするので、備忘代わりに。

ブレース展開

まず、ブレース展開は {} とかで展開する機能。
ブレース(Braces) は { と } の呼称(日本語だと中かっこと呼ぶ感じ)なので、ブレース内をいろいろ触るものと覚えておけば良い感じ。


よく使うのは、次の2通り。


一つ目は、羅列。

# cp /etc/hosts{,_$(date +%Y%m%d)}

これは、次のコマンドに同じ。

# cp /etc/hosts /etc/hosts_$(date +%Y%m%d)

不安な場合は、先に echo しておくと良い。

$ echo /etc/hosts{,_$(date +%Y%m%d)}
/etc/hosts /etc/hosts_20190505

それと2つ目、連続する数字の出力。

$ echo {0..10}
0 1 2 3 4 5 6 7 8 9 10

と書くと、0~10 の数字が連続で出力される。

$ echo {000..10}
000 001 002 003 004 005 006 007 008 009 010

と書くと、000~010 という 0 パディングされた数字が連続で出力される。

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

だと、a~z までの小文字だし、

$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

だと、A~Z までの大文字になる。

途中からの出力 {8..12} や、途中までの出力 {a..f} 等も OK だった。

$ echo {8..12}
8 9 10 11 12
$ echo {a..f}
a b c d e f
$ echo {A..D}{00..15}
A00 A01 A02 A03 A04 A05 A06 A07 A08 A09 A10 A11 A12 A13 A14 A15 B00 B01 B02 B03 B04 B05 B06 B07 B08 B09 B10 B11 B12 B13 B14 B15 C00 C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 C13 C14 C15 D00 D01 D02 D03 D04 D05 D06 D07 D08 D09 D10 D11 D12 D13 D14 D15
$ echo data_{A..C}_201905{01..5}.txt
data_A_20190501.txt data_A_20190502.txt data_A_20190503.txt data_A_20190504.txt data_A_20190505.txt data_B_20190501.txt data_B_20190502.txt data_B_20190503.txt data_B_20190504.txt data_B_20190505.txt data_C_20190501.txt data_C_20190502.txt data_C_20190503.txt data_C_20190504.txt data_C_20190505.txt
$ echo {8..03}
08 07 06 05 04 03

変数展開

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html

これの 「Shell Command Language」内「2.6.2 Parameter Expansion」の部分。
Posix の定義としては「変数拡張」と和訳したほうがヨサゲに見える……
とはいえ、英文から日本語の直訳だと意味が通じにくい部分があるので、ある程度意訳しておく。


以下が、変数展開の記述方法と、その出力結果を意訳したもの。


なお、表名が長いくせに分かりにくいので、先に解説を……
「parameter が Null 以外で設定済」とは、たとえば parameter="A" の状態。定義してあるけど、Null(何もない)以外の状態であるということ。
「parameter が Null で設定済」とは、parameter="" の状態。定義してあるけど、Null(何もない)状態であるということ。
「parameter が未設定」とは、そのものズバリ。まだ定義していない状態。

変数展開記述 parameter が Null 以外で設定済 parameter が Null で設定済 parameter が未設定
${parameter:-word} parameter を利用 word を利用 word を利用
${parameter-word} parameter を利用 null を利用 word を利用
${parameter:=word} parameter を利用 word をアサイ word をアサイ
${parameter=word} parameter を利用 null を利用 word をアサイ
${parameter:?word} parameter を利用 エラー終了 エラー終了
${parameter?word} parameter を利用 null を利用 エラー終了
${parameter:+word} word を利用 null を利用 null を利用
${parameter+word} word を利用 word を利用 null を利用


それぞれの意味は、以下の通り

記述内容 意味
parameter を利用 parameter に定義済みの、値が出力される
word を利用 指定した word で、出力される
null を利用 Null で出力される
word をアサイ parameter に word が代入されつつ、出力される
エラー終了 スクリプトがそこで終了する


ちょっと読み方が特殊だけど、たとえば parameter が Null か未設定のとき、指定した文字列を使いたいというときは ${parameter:-word} を使う。これは、parameter の中身をいじらない。
自分の使い方としては、引数の指定とか。

$ Default="filename"
$ SourceFile=${1:-${Default}}

こうすると、引数の1番目に何も指定されてなくても別途指定しているデフォルト値を利用できる。
もちろん他の使い方もある。

さらに同じ条件だけど parameter の中身に指定した文字列を入れておきたいときは ${parameter:=word} を使う。

#!/bin/bash
set -u
value=$1
num=$((${value:=0} + 1))
echo $num

こんな感じかなぁ?
引数に入力した値の確認とか、計算結果のエラーチェックとか、まったくやっていないけど、使い方があんまり思い浮かばない。もうちょっとマシなサンプルを書ければよかったけど。


それから、変数の中に入っている文字列の長さ(数)を取得する指定は、次の通り。
${#parameter}

$ parameter="example"
$ echo ${#parameter}
7


それから、パターンでの文字列削除方法は、次の通り。

${parameter%word} 最短後置パターンの削除
${parameter%%word} 最長後置パターンの削除
${parameter#word} 最短前置パターンの削除
${parameter##word} 最長前置パターンの削除

パターンにマッチさせる使い方として、ディレクトリを例にする。

SamplePath=/var/log/messages

最短後置パターン(ファイル名もしくは最後のディレクトリ名を消す)

$ echo ${SamplePath%/*}
/var/log

最長後置パターン(この例だと全部消える)

$ echo ${SamplePath%%/*}

最短前置パターン(この例だと最初の / だけ消える)

$ echo ${SamplePath#*/}
var/log/messages

最長前置パターン(ファイル名もしくは最後のディレクトリ名だけ残す)

$ echo ${SamplePath##*/}
messages

ちなみに、ディレクトリ名を消すコマンドに basename がある。
basename コマンドの優位点は、残るファイル名にも加工を掛けられること。
たとえば /usr/local/script/example.sh の、example 部分だけを使いたいとき。

ScriptLog=$(/usr/bin/basename $0 .sh).log

こんな感じで、実行パスからログファイル名を生成するなんてこともできる。


それから、Bash 限定と思われるけど、変数内で置換もできる。

$ Parameter="abce"
$ echo ${Parameter/e/d}
abcd

これは一か所だけなので、全部置換したい場合にはこちら。

$ Parameter="abcefg alphabet"
$ echo ${Parameter//e/d}
abcdfg alphabdt