2018年5月21日月曜日

for文やwhile文を使うとLinux操作が捗る話


LinuxはCUIでの操作が基本となり、GUIでは簡単にはできない繰り返し処理をfor文やwhile文を使うことで、容易に実現できる。

for文やwhile文はシェルスクリプトで使うものと思われがちだが、構文を覚えておけば、ちょっとした繰り返し処理を行う際に、いちいちシェルスクリプトを作らなくても、ターミナルから直接繰り返し処理を実行できるため、効率よく作業ができる。

今回はそんなfor文とwhile文の使い方について記載する。

一定回数繰り返す系

この使い方は、vmstatやiostat等の、繰り返し実行の機能のない性能情報取得コマンドを定期間隔で実行することができるので、パフォーマンス分析を行う際に便利である。

構文は以下の通り。

------------------------------
for i in {1..<繰り返し回数>} ; do <実行コマンド> ; sleep <繰り返し間隔(秒)> ; done

※実行コマンドに日時情報が含まれない場合は、以下の通りdateコマンドを合わせて実行するとよい。
for i in {1..<繰り返し回数>} ; do date ; <実行コマンド> ; sleep <繰り返し間隔(秒)> ; done
------------------------------

以下に、vmstatとiostatを5秒間隔で120回実行した際の実行例を記載する。なお、vmstatはバージョンによっては-tオプションが使えないようなので、その場合はdateを挟んで時刻を表示させるようにすること。

# for i in {1..120} ; do vmstat -t ; sleep 5 ; done
------------------------------
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st                 JST
 3  0      0 1372548   1260 392356    0    0     0     0    4    6  0  0 100  0  0 2018-04-30 14:43:38
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st                 JST
 3  0      0 1372672   1260 392356    0    0     0     0    4    6  0  0 100  0  0 2018-04-30 14:43:43

~(以下略)~
------------------------------

# for i in {1..120} ; do iostat -t -d -x ; sleep 5 ; done
------------------------------
Linux 3.10.0-327.el7.x86_64 (t1110rh72)         2018年04月30日  _x86_64_        (1 CPU)

2018年04月30日 14時44分56秒
Device:  rrqm/s  wrqm/s    r/s    w/s   rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
fd0        0.00    0.00   0.00   0.00    0.00     0.00     8.00     0.00   62.00   62.00    0.00  62.00   0.00
sda        0.00    0.01   0.00   0.06    0.04     0.32    10.96     0.00    0.25    0.38    0.25   0.05   0.00
dm-0       0.00    0.00   0.00   0.07    0.03     0.32     9.68     0.00    0.27    0.44    0.27   0.04   0.00
dm-1       0.00    0.00   0.00   0.00    0.00     0.00    17.99     0.00    0.16    0.16    0.00   0.13   0.00

Linux 3.10.0-327.el7.x86_64 (t1110rh72)         2018年04月30日  _x86_64_        (1 CPU)

2018年04月30日 14時45分01秒
Device:  rrqm/s  wrqm/s    r/s    w/s   rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
fd0        0.00    0.00   0.00   0.00    0.00     0.00     8.00     0.00   62.00   62.00    0.00  62.00   0.00
sda        0.00    0.01   0.00   0.06    0.04     0.32    10.96     0.00    0.25    0.38    0.25   0.05   0.00
dm-0       0.00    0.00   0.00   0.07    0.03     0.32     9.68     0.00    0.27    0.44    0.27   0.04   0.00
dm-1       0.00    0.00   0.00   0.00    0.00     0.00    17.99     0.00    0.16    0.16    0.00   0.13   0.00

~(以下略)~
------------------------------

なお、watchコマンドを使えば、もっと簡単にコマンドの繰り返し実行ができる。ただし、watchコマンドでは、Tera Term等のターミナルソフトのログに実行結果の情報が残らないというデメリットがあるので、きちんとログを残したい場合はfor文で実行する方がよいだろう。

一覧に対してコマンドを実行する系

一覧出力コマンドの結果を引数として、詳細な情報を表示させる際に利用する。たとえば、chageコマンドで各ユーザーのパスワード期限を見る、ethtoolコマンドで各インターフェースの詳細情報を確認する、といった使い方ができる。

構文は以下の通り。

------------------------------
for i in `<一覧出力コマンド>` ; do <実行コマンド> $i ; done
------------------------------

以下にfor文を使って、ユーザーごとにchageコマンドを実行し、パスワード期限設定の一覧を出力させてみる。

# for i in `cat /etc/passwd | cut -d":" -f 1` ; do echo $i ; chage -l $i ; done
------------------------------
root
最終パスワード変更日                         :なし
パスワード期限:                             : なし
パスワード無効化中                           : なし
アカウント期限切れ                           : なし
パスワードが変更できるまでの最短日数         : 0
パスワードを変更しなくてよい最長日数         : 99999
パスワード期限が切れる前に警告される日数         : 7
bin
最終パスワード変更日                         : 5月 24, 2015
パスワード期限:                              : なし
パスワード無効化中                            : なし
アカウント期限切れ                            : なし
パスワードが変更できるまでの最短日数         : 0
パスワードを変更しなくてよい最長日数         : 99999
パスワード期限が切れる前に警告される日数          : 7

~(以下略)~
------------------------------

以下は、ethtoolで同様にインターフェースごとの詳細設定を出力させる構文となる。for文の変数に代入する値は、grepやcutコマンドを使って、必要な箇所(今回でいえばインターフェース名)を取り出すことが重要となる。

# for i in `ifconfig -a | grep "mtu" | cut -d":" -f 1` ; do ethtool $i ; done
------------------------------
Settings for eno16780032:
        Supported ports: [ TP ]
        Supported link modes:   1000baseT/Full
                                10000baseT/Full
        Supported pause frame use: No
        Supports auto-negotiation: No
        Advertised link modes:  Not reported
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Speed: 10000Mb/s
        Duplex: Full
        Port: Twisted Pair
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: off
        MDI-X: Unknown
        Supports Wake-on: uag
        Wake-on: d
        Link detected: yes
Settings for lo:
        Link detected: yes

~(以下略)~
------------------------------

負荷をかける系

性能試験などで負荷をかける際にも利用できる。

構文は以下の通り。sleepを入れずに最短でコマンド実行を繰り返すようにする。

------------------------------
for i in {1..<繰り返し回数>} ; do <実行コマンド> ; done

※無限に繰り返したい場合はwhile文を使う。
while : ; do <実行コマンド> ; done
------------------------------

たとえば、メールサーバーの性能を確認するため、一気にメールを1000通送りたい場合は、以下のように実行する。

# for i in {1..1000} ; do echo "テスト" | mail -s "test subject" -r ex-1@example.com ex-2@example.com ; done

人気の投稿