はじめに
最近、サーバ上での作業の他にも、
JenkinsやDockerなどでもShellスクリプトを書いたりする機会が増えてきていると思います。
社内の人と話していても意外と知られてないけど便利な小技をまとめてみました。
小技
1.ファイルの中身を置換するワンライナー
JenkinsやDockerでちょいちょいと既存の設定ファイルを置換するときに便利です。
こんな感じでよく使っています。
perl -i -pe 's/正規表現1/置換/' 対象ファイル
テストするときは -i
をなしで実行して結果を確認するようにして、
実際に実行する場合に -i
をつけると良いです。
例
# DISABLE PAM
perl -i -pe 's/^UsePAM yes/UsePAM no/' /etc/ssh/sshd_config
2.Shellの変数の中身を置換して利用する
${変数名/置換前/置換後}
という書き方ができます。
こんな感じになります。
% X=abcdefg.json
% echo ${X/.json/.txt}
abcdefg.txt
例
これを使うとファイルの拡張子の一括置換とかに便利です。
% touch {1..5}.json
% ls
1.json 2.json 3.json 4.json 5.json
% for filename in `ls *.json`; do; mv $filename ${filename/.json/.txt}; done
% ls
1.txt 2.txt 3.txt 4.txt 5.txt
3.Shellの変数のデフォルト値を設定する
Shellスクリプトで環境変数を経由して値の入力とするケースは多いと思いますが、デフォルト値を指定しておきたいこともよくあります。
その場合、変数=${変数:-デフォルト値}
という書き方が便利です。
% A=${A:-hogehoge}
% echo $A
hogehoge # 最初は何も値がないので hogehogeになる
% A=${A:-fugafuga} # 同じ変数にデフォルト値 fugafuga をセットする文を書いても
% echo $A
hogehoge # 既にhogehogeがあるので変更されない
4.途中でエラーが発生したら終了するようにする
まじめなShellスクリプトでは、途中でエラーが発生したら終了してしまう方が望ましいことがあります。
途中のSetupスクリプトで実はエラーが発生していたのに、全体では正常終了に見えてしまって不具合に気が付かなかった、、、とかいうことを防ぐためです。
そういうときは set -e
を使うと良いです。
例
例えば、以下の様なスクリプトを実行すると、
echo first
er
echo second
set -e
echo third
er
echo 4th
以下のように、最初の er
というコマンドはエラーですが継続して処理が進みますが、
set -e
した後だと er
でエラーになるので、その後の echo 4th
が実行されません。
% sh a.sh
first
a.sh: line 2: er: コマンドが見つかりません
second
third
a.sh: line 8: er: コマンドが見つかりません
5.catでファイルを作るとき
ヒアドキュメントを使うときに、些細なことですが、 cat <<EOF > /path/to/file
という風に書くことができます。
なんとなくまとまって見やすいので気に入って使っています。
例
% cat <<EOF > /path/to/file
DB_HOST=....
DB_PORT=....
EOF
ヒアドキュメント内で$
で変数を展開したくないときには 'EOF'
という書き方ができます。
% cat <<'EOF' > x
echo $A
EOF
% cat x
echo $A
6.実行ファイルの絶対DIRを取得する
Shellスクリプトの実行時にそのファイルの絶対PATHがほしい時があります。
そういう時には、こういう書き方ができます。
THIS_DIR=$(cd $(dirname $0); pwd)
例
THIS_DIR=$(cd $(dirname $0); pwd)
echo $THIS_DIR
% sh x.sh
/home/k_morishita/tmp
追記:別の方法
コメントで @heliac2000 さんから教えていただきました。ありがとうございます!
そのまま追記しておきます。
realpath
, reallink
コマンドがインストールされていれば以下の様にして絶対パスを得ることができます。
$ pwd
/work/tmp
$ touch a
$ readlink -e a
/work/tmp/a
$ realpath a
/work/tmp/a
7.ファイルの1行目を削除して出力する
先頭のN行、末尾のN行を出力するならば、head
, tail
などが使えますが、
N行名を出力から除外する、というのはわりと悩んでました。
他にも方法があった気がしますが、
sed 1d ファイル名
1〜3行目なら
sed 1,3d ファイル名
とかけば実現できます。
8.Shell UIの小技
履歴を検索
CTRL + R
でコマンド履歴から文字列検索することができます。
これを知っているかどうかでかなり作業効率が変わりますよ。
前にいたDIRにcdする
% cd -
とすると、その前にいたDIRにcd することができます。
前の最後の引数を補完する
ESC
-> .
(ピリオド) の順にキーを押すと、履歴の最後の引数が補完されます。
何度も押すことで履歴をさかのぼります。
% echo hoge
% echo fuga
% echo [ESC -> . と押す] → fuga -> hoge と順に表示される
意外と便利です。
9.定期的にコマンド結果を出力する
例えば、特定のDIRにファイルができるのを目視でチェックしたいということがあります。
気が向いたら ls
を打つとかでも良いですが、
% watch [-n 秒数] ls
とするだけで、デフォルトで2秒間隔でコマンドを実行してくれます。
10.よく使うコマンド群を関数として定義しておく
よく使うコマンドは、 .zshrc や .bashrc などに関数として登録しておきましょう。
例えば、Docker関連だとこういうのが結構重宝します。
# 終了したDockerコンテナを全て削除する
rm_all_docker_containers() {
for id in $(docker ps -a | grep 'Exited ' | awk '{print $1}')
do
docker rm $id
done
}
# 名前が無いDocker Imageを全て削除する
rm_all_docker_none_images () {
docker images | grep '^<none>' | awk '{print $3}' | xargs docker rmi
}
# 実行中のDockerコンテナを全て停止する
stop_all_containers() {
ids=$(docker ps | grep -v 'CONTAINER ID' | cut -d' ' -f 1 )
for id in $ids
do
docker stop $id
done
}
さいごに
他にもあった気がするけど、思い出したら付け足そう。。
(追記)
「perl とか sed とか Shellじゃないじゃん!」というごもっともなツッコミは甘んじて受けさせて頂きます(><
ちょっとタイトルを緩くしました。細かいこと、キニシナイで。