bashの「変数の展開」を利用して左0埋めの連番を作成する方法

投稿者: | 2019年2月24日

はじめに

bashの「変数の展開」を利用して「左0埋めの連番」を作成するコードは以下のようになります。

#!/bin/bash

# 欲しい桁数+1桁の数値を初期値に設定する
count=1001

# 1から10まで
while [ $count -le 1010 ]
do
  # 変数展開で2文字目から末尾までを取得する(左1文字を捨てる)
  echo "${count:1}"

  # 算術展開でカウントアップ
  count=$((++count))
done

色々な事情で外部プログラムが利用できない場合じゃなくても、短いコードなので応用が効きそうです。

連番を付けたファイル名を作成する

#!/bin/bash
  
count=1001

while [ $count -le 1010 ]
do
  touch "sample_${count:1}.txt"
  count=$((++count))
done

ある条件でリストしたファイル名に連番を付ける

#!/bin/bash
# ファイル名「sample.sh」として保存し実行権限を付与する
  
count=1001

for target in $(ls target_*.txt) 
do
  mv $target "target_${count:1}.txt"
  count=$((++count))
done

実行結果

$ COLUMNS=1 ls target_* | sort
target_11126.txt
target_11263.txt
target_16933.txt
target_191.txt
target_23726.txt
target_26724.txt
target_30641.txt
target_31125.txt
target_31319.txt
target_4804.txt

$ ./sample.sh

$ COLUMNS=1 ls target_* | sort
target_001.txt
target_002.txt
target_003.txt
target_004.txt
target_005.txt
target_006.txt
target_007.txt
target_008.txt
target_009.txt
target_010.txt

printfでも良いのでは?

printfコマンドの書式で「%03d」を指定すれば同様の結果を得られます。しかし、結果を変数に代入するためにはサブシェルが必要になります。サブシェルを利用すると内部で子プロセス(cloneによるスレッド)が生成されるため実行速度が遅くなります。

出来るだけ同じ条件で計測すると以下のようになります。(CentOS7.6で確認)

printfの場合

#!/bin/bash
# ファイル名「a.sh」として保存し実行権限を付与する

for c in {1..999}
do
  s=`printf "%03d" $c`
  echo "a-${s}"
done
# 3回計測する

$ time ./a.sh > a.txt

real	0m0.405s
user	0m0.285s
sys	0m0.120s
$ time ./a.sh > a.txt

real	0m0.402s
user	0m0.302s
sys	0m0.099s
$ time ./a.sh > a.txt

real	0m0.408s
user	0m0.289s
sys	0m0.117s

変数の展開の場合

#!/bin/bash
# ファイル名「b.sh」として保存し実行権限を付与する

for c in {1001..1999}
do
  s=${c:1}
  echo "b-${s}"
done
# 3回計測する

$ time ./b.sh > b.txt

real	0m0.012s
user	0m0.007s
sys	0m0.004s
$ time ./b.sh > b.txt

real	0m0.013s
user	0m0.007s
sys	0m0.006s
$ time ./b.sh > b.txt

real	0m0.013s
user	0m0.008s
sys	0m0.004s

999回の変換で、起動から終了までにかかった時間(real)で40倍、プログラム自体の処理時間(user)で30倍の差がでてます。10回の変換では大きな差が出なかったので実行回数が少なければprintfコマンドで良さそうです。

サブシェルで子プロセスが生成されることを確認する

straceの「子プロセスのシステムコールもトレースする」オプション(-ff)と結果をファイルに出力するオプション(-o)を使うと、プロセス毎にログファイルが作成されるので、この機能を利用して確認します。

変換回数を2回にして実行した結果が以下のようになります。

$ strace -ff -o a.log -f ./a.sh
a-001
a-002

$ ls -n a.log*
-rw-rw-r--. 1 1000 1000 9659  2月 25 19:34 a.log.13258
-rw-rw-r--. 1 1000 1000 1465  2月 25 19:34 a.log.13259
-rw-rw-r--. 1 1000 1000 1322  2月 25 19:34 a.log.13260

$ strace -ff -o b.log -f ./b.sh
b-001
b-002

[vagrant@localhost vagrant]$ ls -n b.log*
-rw-rw-r--. 1 1000 1000 7076  2月 25 19:37 b.log.13278

straceのログファイルには末尾にpidが付きます。親プロセスの分を除くと、サブシェルでprintfを利用した側「a.sh」では、2つ多くログファイルが作成されています。

親プロセスのログと思われる「a.log.13258」でシステムコールのcloneを呼び出している場所を確認すると「a.log.13259、a.log.13260」のpidがcloneされた子プロセスだと確認できます。

$ grep -e "clone" a.log.13258
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3fd53c6a10) = 13259
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3fd53c6a10) = 13260