Bashシェルスクリプトで配列の全要素を連結(join)し文字列を生成する

2023-03-27Bash,CentOS,Linux,Mac,Ubuntu

はじめに

紹介してそうで紹介していなかった、Bash シェルスクリプトで配列の全要素を連結して文字列を生成する方法について。

多くのプログラミング言語では、 joinimplode といった関数で提供されている機能になります。

Bash の場合にはこのような機能はどのように実現したら良いでしょうか。

検証環境

$ uname -moi
aarch64 aarch64 GNU/Linux

$ head -n 3 /etc/os-release
PRETTY_NAME="Ubuntu Kinetic Kudu (development branch)"
NAME="Ubuntu"
VERSION_ID="22.10"

$ bash -version | head -n 1
GNU bash, バージョン 5.2.0(1)-release (aarch64-unknown-linux-gnu)

環境変数 IFS ( Internal Field Separator ) を変更する

配列を指定した区切り文字 ( デリミタ ) で連結し、文字列を生成するには環境変数 IFS の値を一時的に変更してやります。

# 配列を定義
$ declare -a LIST=(
  "hello"
  "world"
  "your name"
  "!"
)

特に工夫せずに配列の要素を出力すると以下のようになります。

# 変数名のみを指定し出力した場合
$ echo "${LIST}"
hello

# 配列の要素を指定して出力した場合
$ echo "${LIST[*]}"
hello world your name !

$ echo "${LIST[@]}"
hello world your name !

配列変数は ${L[*]} あるいは ${L[@]} の形式で参照しなければ、最初の要素だけしか出力されない点に注意です。

何もしない場合には *@ のいずれを添字に指定した場合でもスペース区切りで全要素が出力されます。

今度は IFS 環境変数に区切り文字として カンマ ( , ) を指定した後、配列変数の値を出力してみます。

$ IFS=','

# 添字に * を指定し、ダブルクォートで囲んだ場合
$ echo "${LIST[*]}"
hello,world,your name,!

# 添字に * を指定し、ダブルクォートで囲まなかった場合
$ echo ${LIST[*]}
hello world your name !

# 添字に @ を指定し、ダブルクォートで囲んだ場合
$ echo "${LIST[@]}"
hello world your name !

# 添字に @ を指定し、ダブルクォートで囲まなかった場合
$ echo ${LIST[@]}
hello world your name !

想定した通りの出力が行われるのは、 echo "${LIST[*]}" と指定した場合でした。

* を使用する、 ダブルクォートを使用する、という点が少し覚えづらいです

環境変数 IFS の変更を一時的なものにする方法

IFS 環境変数を使って、配列変数を join して文字列を生成できました。
しかし、今回の方法は注意が必要です。
IFS 環境変数を変更してしまうと、それ以降の操作も IFS 環境変数を変更した挙動が続きます。

これを回避するために ( ... ) で囲んで、 サブシェル の中で IFS 環境変数を変更・利用します。

# サブシェルの中で IFS を変更し、利用
$ (IFS=','; echo "${LIST[*]}")
hello,world,your name,!

# サブシェルを抜けるともともとの挙動に戻る
$ echo "${LIST[*]}"
hello world your name !

# サブシェルを抜ける IFS の値がカンマではなくなっている
$ echo "${IFS}"

ひとこと

配列関連のエントリのあとにいつもいっていますが、 シェルスクリプトで配列を使うことあまりないんですよね。

2023-03-27Bash,CentOS,Linux,Mac,Ubuntu