目次
はじめに
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