- 投稿日:2019-11-30T23:52:02+09:00
シェルスクリプトTIPS
シェルスクリプトのTIPS
最近はPyrhonやRubyなど他のスクリプト言語も流行っていますが、Unixエンジニアと言えばシェルスクリプト。この記事は基本的なシェルスクリプトがかける人向けのTIPSです。他人が書いたシェルスクリプト読んでいて、「なんだこれ?」と思った時の参考にして下さい。
◆安全なSHEBANGについて(/bin/shの実体)
昨今、多くのUnixOSの/bin/shはBashとなっているが、実はすべてのUnix/Linuxがそうなっているとは限らない。AshだったりZshだったりDashだったりの可能性もある。なので、BASH固有の機能などを利用しているシェルスクリプトの場合、一行目に書くシェバンは、シェルを明示しておく。
#!/bin/bashもしくはすべてのUnixシェルで実行できるよう古典的で安全な(PORTABLEな)シェルスクリプトを記述しておくと良い。ただし、実行速度が低下したり、配列が使えない、EXPRコマンドを用いたインクリメントがトリッキーなどの制限がある。
◆DEBUGモード
どこでエラーが起きているのかわからないが正常終了しない場合、Xオプションを付けてDEBUGモードで実行する。シェルプログラミングの場合、これとPRINTFデバッグで大方は解決する。
# /bin/bash -x myscript.sh◆インクルード
ドットの後にパラメータや自作の関数などを定義したファイルを指定すれば、実行時に読み込まれる。パラメータ専用ファイルなどを作成し、プロジェクトごとに編集すれば直接スクリプトを編集するより安全にカスタマイズ・デプロイが可能となる。
#!/bin/bash . /usr/dev/param . /user/dev/functions #(以下省略)◆ワンライナー1 セミコロンで複数行を1行に収める
for name in foo bar foo2 do echo ${name} doneセミコロンを用いて一行で書くと次のように表せる
for name in foo bar foo2; do echo ${name}; doneワンライナーの良い点として可読性は低下するがコマンドラインで気軽に投入が可能で、高度なコマンドを運用マニュアルに組み込むことができる。
◆ワンライナー2 異なる記述で同じ処理をさせる
if [ -e /var/tmp/test ] then rm /var/tmp/test fi前のコマンドが正常終了(終了コードはTrue)した場合、&& 以降の処理を実行する。
頻出の記述法なので知っておくと〇[ -e /var/tmp/test ] && rm /var/tmp/fileよく知られた話だが if [ ] then ~ の [ は、実体は /bin/test のエイリアス(別名)である /bin/[ であり、次のように記述することができる
/bin/test -e /var/tmp/test && rm /var/tmp/file&&と逆で、直前のコマンドが異常終了(終了コードがTrueでない)した場合、|| 以降の処理を実行する。
[ の後の ! はNOTを意味し、ファイルが存在しない場合を表す。# testファイルが存在しない場合、作成しなさい if [ ! -e /var/tmp/test ] then touch /var/tmp/test fi下のように書くことが可能。
[ -e /var/tmp/test ] || touch /var/tmp/test◆&&と||を利用したワンライナーのハマりポイント
よくやる間違いで下記のコードは意味が異なるので注意する。
test_Aが成立している場合、コマンドBを実行し、そうでない場合はコマンドCを実行するIf [ test_A ] then command_B else command_C fi[ test_A ] && command_B || command_C上記ではtest_Aが成立している場合、コマンドBを実行する、そうでない場合は、コマンドCを実行する。ところが、test_Aが成立しておりコマンドBの実行した後、コマンドBの実行結果が失敗だった場合はコマンドCが実行されてしまう (ココがハマリポイント)
◆逆ワンライナー 1行で書ける処理を複数行に分割する
スクリプトの可読性を高める場合に利用される。
cat /var/tmp/test.log | grep -v ^# | sed -^#/d | egrep 'foo1|foo4|foo5' | awk '{print $1,$3,$7,$11}' | sort -e | sed -e 's/foo/bar/g' > /var/tmp/tmp.log.txtcat /var/tmp/test.log | \ grep -v ^# | \ sed -e '^$/d' | \ egrep 'foo1|foo4|foo5' | \ awk '{print $1,$3,$7,$11}' | \ sort -u | \ sed -e 's/foo/bar/g' \ > /var/tmp/tmp.log.txt◆[ と [[ の違い
シェルスクリプトでは正規表現と組み合わせて[[がパターンマッチングで利用されます。
クォートしていない場合、パターンマッチングの結果、fooはfo[ou](fooもしくはfouのどちらか)に該当するためTrueが返されます。
ただし、[[でもクォートしている場合、文字列解釈の結果は変わらないためFalseが返されます。# 文字列はアンマッチ (bash)# [ qiita == 'qiit[aiueo]' ]; echo $? 1 # 文字列はマッチ (bash)# [[ qiita == qiit[aiueo] ]]; echo $? 0 # 文字列はアンマッチ (bash)# [[ qiita == 'qiit[aiueo]' ]]; echo $? 1その他に大きな違いとしては、文字列のパターンマッチングに利用される他、[[の場合、文字列に数式が書かれている場合は文字列と解釈せず、計算を行ってからテストが実行される。こちらも覚えておくと◎。
(bash)# char='10 + 10' (bash)# [ ${char} -eq 20 ]; echo $? -bash: [: 10+10 integer expression expected (bash)# [[ ${char} -eq 20 ]]; echo $? 0◆インクリメント・デクリメント
外部コマンド expr を用いて計算。exprのプロセスをforkするコストが高いためスクリプトの実行速度はやや遅くなるが、どのUnixシェルでも動作するため安全でポータブル性が高い書き方となる。
cnt=`expr ${cnt} + 1` cnt=`expr ${cnt} - 1`BASHのビルドイン機能。以下の3つの書き方だと実行結果は高速だが、他のシェルで動かない場合もあるため、BASHの脆弱性が見つかった場合などに他のシェルに切り替えて実行するなどの対策は打てなくなるというデメリットがある。
((cnt++)) let cnt++ let ++cnt ※デクリメントは--にしてください◆ユーザによる入力を受け付ける
ITやCASEを利用してユーザによる値の入力や選択を記述できる。入力文字列を表示させたくない場合はSオプションを利用する。
(bash)# read str aiueo <ENTER> (bash)# echo ${str} aiueo◆分岐先で何もしたくない時はコロン
if elseやcaseにいろいろ分岐を書いていたけれど、いくつかの処理をとりやめたい場合でも、後に再度利用しそうだったり、条件式やパラメータなどが非常に重要で消したくなかったりする場合、その分岐構造そのものは残しておきたかったりする場合があります。そういう場合は処理部をコメントアウトし替わりに : を入れておきます。
if [ -e /usr/dev/functions ] then . /usr/dev/functions else # (停止したい処理はコメントアウト) : fi◆yesコマンド
文字列yと終了コード0を超高速で延々と返す謎のコマンドyes。
(bash)# yes y y y y使う場面が思い浮かばないと思いきや、無限ループさせたい場合に使ったりします。
#!/bin/bash . /usr/dev/functions . /usr/dev/param while [ yes ] do sleep ${interval} chk=$(ps -ef | grep -c ${srvs}) if [ ${chk} -lt 2 ] then # function alert_to aleat_to all_engineer@foobar.co.jp # function reboot_services reboot_services ${srvs} fi done◆スクリプトの簡単なDaemon化
常駐化させてデーモン化(サービス化)させたい場合なんかにはnohupを使います。以下のスクリプトですと、process_chk.shはログアウト後も常駐プロセスとなり働き続けてくれます。
(bash)# nohup /bin/bash process_chk.sh &◆スクリプトの実行結果を画面でも見たいし、同時にファイルにも保存したい
(bash)# ./chk.sh | tee -a /var/tmp/result.log OK OK NG◆精密な計算を行いたい
exprコマンドは整数しか扱えないため、小数点何桁といった計算を行うにはbcコマンドを利用する。デフォルトでは小数点以下は表示しないので、scaleで桁数を指定する。
#!/bin/bash result=$(echo "scale=1; 101354 * 1.08" | bc) echo ${result}◆ランダムな数値を得る
BASHのシェル変数を利用するパターン
(bash)# echo ${RANDOM} 8732BASHの機能を利用せず安全でポータブルな方法を検討した結果、時間の文字列でハッシュをとれば良いんじゃないかというアイデアが上手くいった
(bash)# date | md5sum | tr -dc "0123456789" 87329348◆ランダムな文字列を得る
先ほどの手法を再利用して、ランダムな文字列を得る。シンプルな出し方のため、パスワードとして利用するには向かない。パスワードとして利用するにはdateコマンドのオプションを工夫するか、次の方法を利用する。
(bash)# date | md5sum | tr -dc "abcdefghijklmnopqrstuvwxyz" ndrtnrvlkjr◆暗号強度の高い文字列を得る
先ほどの手法で任意のパスワードなどを生成することができるが、パスワードとして利用するには類推可能であるため、ここで得た文字列にもう少し高度な処理を施しておくと、事前計算による類推や一覧表からの抽出は使えなくなりより強度が増します。具体的にはパスワードソルトを指定しておきます。
(bash)# SALT=hogehoge (bash)# echo "$(date)${SALT}" | md5sum | tr -dc "abcdefghijklmnopqrstuvwxyz" fdsjlnelr◆typesetとdeclare
どちらも同じ変数を宣言する命令。typedefはKSHなど他のシェルでも利用が可能で古典的かつポータブル。declaireの方が新しい。
オプション 内容 a 配列を宣言 i 数値を宣言 r 読み出し専用を宣言 p 宣言した変数の値を表示 他にもあれば適当に更新の予定。
- 投稿日:2019-11-30T21:13:24+09:00
[linux] デフォルトアプリの設定変更
デフォルトのファイルマネージャー等が実行できない(Failed to exucute child process ) とエラーが出たときの対処法となります。
windowsでいう「既定のアプリを設定する」は、linuxでは「設定マネージャー」→「お気に入りのアプリケーション」から変更できます。
- 投稿日:2019-11-30T16:15:43+09:00
【AWS】EC2インスタンスでswapサイズを増やす手順
EC2インスタンスでswapサイズを増やす手順
PostgreSQLを使って処理を回していたらあるときからOOMキラーが発動するようになった。
単純にはインスタンスタイプを上げてメモリを増やせば解決するけど、それだと費用は倍になるし、オーバースペックになる。
ふと思ってswapサイズを見てみたら「0.8GBしかないじゃん!?」となったのでswapサイズを増やすことにした。
なお、今回はパーティションを増やしたくないため拡張で対応しているが、新しくパーティションを切る方法もある。事前準備
下記の図のようにswapサイズをデフォルトの0.8GBから30GB拡張する。
ルートボリュームを30GB拡張、ファイルシステム拡張、LVM拡張、swap拡張、というように下から順にやれば良い。
初期状態のルートボリューム30GBではこのような状態になっている。[root@apple ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 30G 0 disk ├xvda1 202:1 0 500M 0 part /boot ├xvda2 202:2 0 7.5G 0 part │├VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm / │└VolGroup-lv_swap (dm-1) 253:1 0 816M 0 lvm [SWAP] └xvda3 202:3 0 22G 0 part └VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm /AWSコンソールからルートボリュームのサイズを30GBから60GBに増やすと /dev/xvda が60GBに増える。
[root@apple ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 60G 0 disk ├xvda1 202:1 0 500M 0 part /boot ├xvda2 202:2 0 7.5G 0 part │├VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm / │└VolGroup-lv_swap (dm-1) 253:1 0 816M 0 lvm [SWAP] └xvda3 202:3 0 22G 0 part └VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm /パーティション、ファイルシステムを拡張する
/dev/xvda が30GB増えたので、パーティション /dev/xvda3 を30GB増やしたい。
パーティション拡張にはgrowpartコマンドを使用する。# growpartインストール [root@apple ~]# yum install growpart # epelにあるが、ファイルがNot Foundとか言われたらパスが変わっているかもしれない # そういうときはパスを確認してwgetで取ってくれば良い [root@apple ~]# wget ~~~/cloud-utils-growpart-0.27-10.el6.x86_64.rpm [root@apple ~]# rpm -ivh cloud-utils-growpart-0.27-10.el6.x86_64.rpm # ロケールが "en_US.UTF-8" じゃないと失敗するのでロケール変更 [root@apple ~]# export LC_ALL="en_US.UTF-8" # growpart実行 # パーティション番号はスペースを空ける [root@apple ~]# growpart /dev/xvda 3 CHANGED: partition=3 start=16777216 old: size=46133324 end=62910540 new: size=109043864,end=125821080 # オンラインでのresize2fsはできないので再起動する # 起動時に自動的にresize2fsしてくれる [root@apple ~]# reboot # パーティション拡張を確認 [root@apple ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 60G 0 disk ├xvda1 202:1 0 500M 0 part /boot ├xvda2 202:2 0 7.5G 0 part │├VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm / │└VolGroup-lv_swap (dm-1) 253:1 0 816M 0 lvm [SWAP] └xvda3 202:3 0 52G 0 part └VolGroup-lv_root (dm-0) 253:0 0 26.7G 0 lvm //dev/xvda3 が22GBから52GBに増えているのでヨシ!
LVMを拡張する
パーティションのサイズが増えたのでまずPV、VGを拡張する。
# 拡張前 [root@apple ~]# pvdisplay /dev/xvda3 --- Physical volume --- PV Name /dev/xvda3 VG Name VolGroup PV Size 22.00 GiB / not usable 2.04 MiB Allocatable yes PE Size 4.00 MiB Total PE 5631 Free PE 511 Allocated PE 5120 PV UUID XAzVVN-F4vG-XWcq-Ay61-4v2A-Hynk-hcmKja # PV拡張 [root@apple ~]# pvresize /dev/xvda3 Physical volume "/dev/xvda3" changed 1 physical volume(s) resized / 0 physical volume(s) not resized # 拡張後 [root@apple ~]# pvdisplay /dev/xvda3 --- Physical volume --- PV Name /dev/xvda3 VG Name VolGroup PV Size 52.00 GiB / not usable 3.07 MiB Allocatable yes PE Size 4.00 MiB Total PE 13310 Free PE 8190 Allocated PE 5120 PV UUID XAzVVN-F4vG-XWcq-Ay61-4v2A-Hynk-hcmKja # VG確認 [root@apple ~]# vgdisplay --- Volume group --- VG Name VolGroup System ID Format lvm2 Metadata Areas 2 Metadata Sequence No 6 VG Access read/write VG Status resizable MAX LV 0 Cur LV 2 Open LV 2 Max PV 0 Cur PV 2 Act PV 2 VG Size 59.50 GiB PE Size 4.00 MiB Total PE 15232 Alloc PE / Size 7042 / 27.51 GiB Free PE / Size 8190 / 31.99 GiB VG UUID KKH8X5-ZgIG-drSY-yYeX-znc7-APSq-kCVFJmPV Size、Free PEともに30GB増えている。
またVGはPVの拡張にともなって自動的にVG Size、Free PEが増えている。swapサイズを拡張する
最後にswapが乗っているLV、およびswapサイズを拡張する。
# LV確認 [root@apple ~]# lvdisplay --- Logical volume --- LV Path /dev/VolGroup/lv_root LV Name lv_root VG Name VolGroup LV UUID xL8jFY-tYoP-Akp8-wiLV-PbUm-5YdW-jc7kTQ LV Write Access read/write LV Creation host, time localhost.localdomain, 2014-08-20 14:05:30 +0900 LV Status available # open 1 LV Size 26.71 GiB Current LE 6838 Segments 2 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 --- Logical volume --- LV Path /dev/VolGroup/lv_swap LV Name lv_swap VG Name VolGroup LV UUID 0hYwIt-mA9J-Mfhw-x9fc-jmRd-520Q-OMLhQ0 LV Write Access read/write LV Creation host, time localhost.localdomain, 2014-08-20 14:05:32 +0900 LV Status available # open 1 LV Size 816.00 MiB Current LE 204 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:1 # swapサイズ確認(現在0.8GB) [root@apple ~]# swapon -s Filename Type Size Used Priority /dev/dm-1 partition 835580 0 -1 # いったんswapを無効化 [root@apple ~]# swapoff /dev/VolGroup/lv_swap # LV拡張 [root@apple ~]# lvextend -L+30G /dev/VolGroup/lv_swap Extending logical volume lv_swap to 30.80 GiB Logical volume lv_swap successfully resized # swapの再作成、再有効化 [root@apple ~]# mkswap /dev/VolGroup/lv_swap mkswap: /dev/VolGroup/lv_swap: warning: dont erase bootbits sectors on whole disk. Use -f to force. スワップ空間バージョン1を設定します、サイズ = 32292860 KiB ラベルはありません, UUID=81ef3a01-380d-4305-bf2f-bdb7bdac39bf [root@apple ~]# swapon /dev/VolGroup/lv_swap # swapサイズ再確認(30GB増えている) [root@apple ~]# swapon -s Filename Type Size Used Priority /dev/dm-1 partition 32292860 0 -1swapサイズは無事拡張されたが、このあと /etc/fstab を修正する必要がある。
/etc/fstab には /、/boot、swap がそれぞれUUIDで記載されているが、mkswapしたときに値が変わっているのである。[root@apple ~]# vi /etc/fstab UUID=d748f0cb-bf25-402c-9a58-3f6dd4a3b5d7 / ext4 defaults 1 1 UUID=833e1f14-6c25-4430-8776-d4ad024ee760 /boot ext4 defaults 1 2 UUID=9c8c7b9c-a4d6-4217-a498-5816b5e7e00b swap swap defaults 0 0 tmpfs /dev/shm tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0 # UUIDをパーティションに直すと下記のようになる /dev/mapper/VolGroup-lv_root / ext4 defaults 1 1 /dev/xvda1 /boot ext4 defaults 1 2 /dev/VolGroup/lv_swap swap swap defaults 0 0 tmpfs /dev/shm tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0以上。
- 投稿日:2019-11-30T11:44:50+09:00
git pull したら permission deniedになったから、その解決法をメモ
開発環境用のサーバーで、git pull したら、
permission denied でエラーになった。まさしくこのような感じのエラー
$ git pull error: cannot open ファイル名: Permission deniedエラーの原因としては、自分が使用しているユーザーはそのディレクトリを変更する権限を持っていないからが理由のよう。
試しにファイルの権限を確認
$ ll -rw-r--r-- 1 hoge hoge 5742 10 26 2018 test1.php -rw-r--r--@ 1 fuga hoge 6807 10 12 2018 test2.php -rw-r--r-- 1 hoge hoge 65 10 11 2018 a1.php -rw-r--r-- 1 fuga hoge 65 10 11 2018 a2.php本来であれば、hogeユーザーで作業する決まりだが、
fugaユーザーで作業した人がいたみたい。。なら、ファイルの権限を変更すればOKと思いきや、
修正するファイルは、プロジェクトのいたるところにあり、
おまけに.git配下も修正しなくてはいけず、いちいち該当するファイルのパスを指定して権限を変更していたら日が暮れるという感じ。。まとめてファイルの権限を変更
ということで、便利なlinuxコマンドもメモしておく
ルートディレクトリで作業
//ファイル find ./ -user fuga -type f -print | sudo xargs chown hoge:hoge // ディレクトリ find ./ -user fuga -type d -print | sudo xargs chown hoge:hoge //シンボリックリンク find ./ -user fuga -type l -print | sudo xargs chown hoge:hoge
これでまとめて権限を変更することが可能。
実行する流れとしては、
find ./ -user fuga -type f -printで該当するファイル一覧を確認し
find ./ -user fuga -type f -print | sudo xargs chown hoge:hogeで権限をまとめて変更
find ./ -user fuga -type f -print再び、こちらを実行し、ファイルが表示されないことを確認って感じ。
こういうコマンドをすらすら思いつけるようになりたい。