最近覚えたシェルスクリプトの小ネタ
シェルスクリプト神から教えていただいた。忘れないように書いとく
(追記)聞いて、自分の記憶した内容をそのまま書いちゃったので、ちゃんとマニュアル通りか確認してなかったので反省
$ man bash
目次
- xargsでfunctionを叩く
- 連想配列もどき
- 変数間接参照
- なんでもかんでもawkで整形しない
- 文字列の末尾から数えて○文字目を△文字取り出す
- 番外編:やたら
if [ ]; then
を使わない
※Markdownで目次の書き方がわからんかった
xargsでfunctionを叩く
下の例だと find xxxx | xargs cp xxxx
ってかけば良さそうでイマイチだけど、もっと複雑な処理やらせたいときに。
- 前はこう書いてた
#!/bin/bash for x in `find /var/www -name xxx` do cmd="cp -v /hoge/fuga/xxx $x" [[ `md5sum $x | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | : done
- xargsで5多重で同じことやらせる
#!/bin/bash function sample() { cmd="cp -v /hoge/fuga/xxx $1" [[ `md5sum $1 | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | : } # これしたらxargsから呼べることをしらなかった export -f sample find /var/www -name xxx | xargs -P5 -I{} bash -c "sample {}"
連想配列もどき
# 連想配列もどきのHを定義 declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" for x in a b c d e do # とりだす echo ${H["$x"]} done
ちなみに、
- こっちは配列見える
declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" function sample(){ # 682f7xxxxxxxxxxxxxxxxxxx0 echo ${H["a"]} } sample
- こっちは配列見えない
#!/bin/bash declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" function sample(){ # 682f7xxxxxxxxxxxxxxxxxxx0 echo ${H["a"]} echo "hoge" } # ここで配列はexportされないらしいけど、あんまり詳しく調べてない。当たり前の動き? export -f sample find ~/test -type f | xargs bash -c "sample"
変数間接参照
#!/bin/bash var1="xxxxxxxxxxxxxxxx" var2="yyyyyyyyyyyyyyyy" var3="zzzzzzzzzzzzzzzz" for i in `seq 1 3` do # 変数を使って作る変数名は一度変数に入れなきゃだめ tmp="var${i}" echo ${!tmp} done
なんでもかんでもawkで整形しない
/home/hoge/fuga/piyo/php5.2.cgi
のようなパスから、末尾の php5.2.cgi
のみ抽出し、かつその値を利用して色々処理したい時に、つい以下のようなことを書いてた。
[root@hoge ~]# cat test.sh #!/bin/bash for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"` do echo $x | awk -F/ '{print $10}' done
このくらいのことをawk出してるとコストが高いので、bashの変数操作を使って以下の様に書く
[root@hoge ~]# cat test2.sh #!/bin/bash for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"` do echo ${x##/*/} done
すると
[root@hoge ~]# time ./test.sh ・・・ real 0m8.316s user 0m2.367s sys 0m7.828s [root@hoge ~]# time ./test2.sh ・・・ real 0m0.763s user 0m0.357s sys 0m0.394s
圧倒的に早い!!!
捕捉 この例だと
find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi" | awk -F/ '{print $10}'
でいいけど、今回のこれは、①findしたパス②awkで整形した値両方を使用して、いろいろ処理する必要があって、forを利用してた
文字列の末尾から数えて○文字目を△文字取り出す
以下は乱暴な例ですが、 5.x
の x
の値に応じて処理したい要件があった。
先頭の文字はファイルによってバラバラの長さのため、cutコマンドでは取得できないため、「末尾から何文字目」という指定をしたかった
[user ~/work/test]$ ll total 0 -rw-r--r-- 1 user staff 0B 6 28 00:49 hogehogeho_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 xxxx.yyyy-abcdefghijk_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 xxxxxxxxxxxxxxxxxxxxxxxxxxx_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 yyyyyyyyyyyy-aaaaaaaaaaaaaaaa_ver5.3.sh [user ~/work/test]$ for x in `find . -type f` > do > echo ${x:${#x}-4:1} > done 2 2 2 3
番外編:やたら if [ ]; then
を使わない
testコマンドを使うとフォークしてしまって大量に叩くとパフォーマンス悪いとのこと。
[PMAC226S ~/work]$ cat test.sh #!/bin/bash for i in `seq 1 1000000` do if [ "$a" = "hoge" ]; then echo "hoge" else echo "fuga" fi done
と
[PMAC226S ~/work]$ cat test2.sh #!/bin/bash for i in `seq 1 1000000` do [[ "$a" = "hoge" ]] && echo "hoge" || echo "fuga" done
があった時、後者の方が結構はやい
[PMAC226S ~/work]$ time ./test.sh ・・・ real 0m30.164s user 0m20.777s sys 0m2.917s [PMAC226S ~/work]$ time ./test2.sh ・・・ real 0m25.246s user 0m17.777s sys 0m1.812s