Linux
shell
sh

【shell】シェルスクリプト作成時のtips【Linux】

はじめに

Linux/UNIX上で作業する際にちょっとしたツールを作成したり、定型的なジョブを実行するための運用ツールを作ったりといったことがあるかと思います。
このようなときに役立つ方法・コマンドを紹介します。

ネタがあり次第、随時更新予定です。

tips

basename : フルパスからファイル名のみを抽出する・ファイル名から拡張子を除く

basenameを活用しましょう。

sedやawkを活用して取り出すことも可能ですが、basenameを利用すれば簡単です。
フルパスからファイル名を取りだす、またさらに拡張子を除去する、といった操作ができます。

まず、以下のようなファイルが存在すると仮定します。

$ find /path/to/targe
/path/to/targe/file1.csv
/path/to/targe/file1.csv

下記のスクリプトを作って実行します。

#!/bin/sh

for file in `ls /path/to/target/*.csv`
do
    echo `basename ${file}`
    echo `basename ${file} .csv`

done

実行結果は下記のようになります。

file1.csv
file1
file2.csv
file2

mktemp : テンポラリファイルの作成

mktempを活用しましょう。

APIからのレスポンスに対して、2回以上grep等の検索をしたい場合など、一時的にファイル保存が必要なケースがあります。
プロセスIDなどを元に一時的なファイル名を生成することも可能ですが、mktempを利用すれば簡単です。

また既存ファイル名と同一のファイルを作らないため、シンボリックリンク攻撃に対しても安全です。

上記をいろいろと考慮すると結構なコード量になってしまうので、
対象ファイルを簡単にセキュアに保つ必要がある場合は、これを使った方が良いです。

まず、どのような挙動になるか、コマンドラインから実行してみます。

### mktempを実行する
$ mktemp
/tmp/tmp.QnbBGwvXJF
$ ls -la /tmp
total 4
drwxrwxrwt.  7 root    root     109 Dec 23 15:34 .
dr-xr-xr-x. 18 root    root    4096 Dec 10 07:02 ..
drwxrwxrwt.  2 root    root       6 Jun 29  2016 .font-unix
drwxrwxrwt.  2 root    root       6 Jun 29  2016 .ICE-unix
drwxrwxrwt.  2 root    root       6 Jun 29  2016 .Test-unix
-rw-------.  1 vagrant vagrant    0 Dec 23 15:34 tmp.QnbBGwvXJF
drwxrwxrwt.  2 root    root       6 Jun 29  2016 .X11-unix
drwxrwxrwt.  2 root    root       6 Jun 29  2016 .XIM-unix

### 現在のプロセスのuid, gidを確認する
$ id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant),10(wheel)

mktempコマンドの実行後、生成されたテンポラリファイルのフルパスが標準出力に出力されます。

対象のファイルを見てみると、owner, groupとも実行プロセスのuid, gidが設定され、パーミッションは600となります。
似たようなことを実装するのは可能ですが、mktempで済ませたほうがスマートです。

スクリプトから実行する場合は、下記のようにmktempをコマンド置換して変数に代入すれば良いでしょう。

#!/bin/sh

TMP_FILE=`mktemp`

anycommand > ${TMP_FILE}

grep nnn ${TMP_FILE}
grep sss ${TMP_FILE}

psコマンドの結果からgrepを除外する

例として、プロセス一覧から「python」プロセスのみを抽出してみましょう。
psコマンドの結果に対して「grep -v grep」を実行し、当該行を除去する方法が定番かと思います。

$ ps -ef | grep python | grep -v grep
root      1011     1  0 15:11 ?        00:00:00 /usr/bin/python -Es /usr/sbin/tuned -l -P
vagrant   3277  3245  0 15:14 pts/1    00:00:00 python

しかし、以下のように実行することでgrepコマンドの実行を減らすことができます。

$ ps -ef | grep pytho[n]
root      1011     1  0 15:11 ?        00:00:00 /usr/bin/python -Es /usr/sbin/tuned -l -P
vagrant   3277  3245  0 15:14 pts/1    00:00:00 python

何故でしょうか。
まず、当該grepコマンドに渡される引数は、psコマンド上からは下記のように確認できます。
コマンドラインから指定した文字列がそのまま表示されます。

[vagrant@stdsv1 ~]$ ps -ef | grep gre[p]
vagrant   3299  3261  0 15:26 pts/2    00:00:00 grep --color=auto gre[p]

grepは、引数をマッチングパターンとして認識しますので、
「[]」に囲まれた文字列は、そのうちのいずれかの一文字にマッチするものとみなします。

整理すると、「grep pytho[n]」と実行したとき、以下のような挙動となります。

  • psコマンドの実行結果上、grepコマンドのプロセスは「grep --color=auto pytho[n]」となる
  • grepコマンド自体は、「python[n]」というマッチングパターンに一致する文字列のみ(=pythonに一致する文字列のみ)を抽出して出力する。