技術メモのかけら

内容はもとより調べたことすら忘れてしまうので個人的なメモです。とにかく短く、結論だけ書いていきます。

ファイルからパターンにマッチした範囲の行を取得する

sedの場合はsed -n '/開始パターン/,/終了パターン/p' で、開始パターンにヒットした行から終了パターンの行の範囲を取得できる。

$ seq 10 | sed -n '/5/,/8/p'
5
6
7
8

awkawk '/開始パターン/,/終了パターン/' で書けるのでこっちの方が簡単である。

$ seq 10 | awk '/5/,/8/'
5
6
7
8

ログの確認に使うと捗りそうです。

既に存在しないディレクトリでコマンドを実行したときのエラー

ターミナルで作業中に、カレントディレクトリが他プロセスにより削除された後でコマンドを打つとエラーになることがあります。
冷静にメッセージを見れば察しはつくのですが、余裕がない時に起きるとちょっとビックリする。

  • pwdコマンドは削除済みのフォルダであっても特にエラーにならない
$ ls /tmp
#/tmp以下にディレクトリは存在しない

$ pwd
/tmp/work
$ mkdir hoge
mkdir: cannot create directory `hoge': No such file or directory

$ touch hoge
touch: cannot touch `hoge': No such file or directory
$ psql
could not identify current directory: No such file or directory
could not identify current directory: No such file or directory
/usr/bin/psql: could not find own program executable
$ java version
Error occurred during initialization of VM
java.lang.Error: Properties init: Could not determine current working directory.
        at java.lang.System.initProperties(Native Method)
        at java.lang.System.initializeSystemClass(System.java:1166)
  • npmコマンド
npm --version
path.js:1163
          cwd = process.cwd();
                        ^

Error: ENOENT: no such file or directory, uv_cwd
    at Object.resolve (path.js:1163:25)
    at Function.Module._resolveLookupPaths (module.js:408:17)
    at Function.Module._resolveFilename (module.js:480:22)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at /root/.nodebrew/node/v8.1.3/lib/node_modules/npm/bin/npm-cli.js:19:21
    at Object.<anonymous> (/root/.nodebrew/node/v8.1.3/lib/node_modules/npm/bin/npm-cli.js:92:3)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)

pg_dumpのZオプションで作ったファイルの解凍&リストアにいつも混乱する

pg_dumpコマンドに、 -Z 9 のようにZオプションを与えるとダンプファイルがgzipで圧縮できます。

$ pg_dump -f /mydb.sql -Z 9 mydb

-F でフォーマットを指定しない場合、plain形式(要はSQLファイル)でダンプ&gzipで圧縮されるだけなんですが、なぜか私は「pg_restoreでリストアしなきゃ」という思考回路になっているようですorz 一瞬混乱した後に、gunzipして解凍しなきゃと思うのですが、今度は次の問題にハマります。

$ gunzip mydb.sql
gzip: mydb.sql: unknown suffix -- ignored

メッセージの通り、拡張子が .gz じゃないとNGってだけなんですが、これも一瞬???となります。
別に拡張子なんてなんでも良いだろと思うのですが、、、 次からは素直に.gzで出力しよう。

こんなことでハマるのは私だけと思いますが、2度もハマったのでメモしておきました。

bashのpipefailオプション

以前から set -e を使って、エラー時に実行を止めるシェルをよく書いてましたが、
パイプでコマンドを繋げた場合、最後のコマンドにしか効かないことを今更ながら知ったのでメモ。

例えば、以下のシェルはslなんてコマンドは存在しないので、そこでエラーになり止まると思いきや、、、

#!/bin/bash

set -e

sl | echo "パイプの最後のコマンド"
echo "戻り値を確認:$?" 
echo "ここには来ないはず"

後続の処理が実行されてしまいました。

$ bash /tmp/sete.sh
パイプの最後のコマンド
/tmp/sete.sh: 行 5: sl: コマンドが見つかりません
戻り値を確認:0
ここには来ないはず

パイプの最後のechoが正常終了してるので、パイプ実行直後の$?が0となり後続が実行されるようです。

次にset -eの行をset -eo pipefailに変えて実行してみます。

$ bash /tmp/sete.sh
/tmp/sete.sh: 行 5: sl: コマンドが見つかりません
パイプの最後のコマンド

今度はパイプの最後までは実行されるが、後続処理は実行されませんでした。
上記を実行直後に$?を確認すると、コマンドが見つからない旨のエラーコード127が返っているので処理が止まるようです。

今度は、2箇所でエラーになるように絶対にヒットしないgrep detarameを追加してみます。

sl | grep detarame | echo "パイプの最後のコマンド"
$ bash /tmp/sete.sh
パイプの最後のコマンド
/tmp/sete.sh: 行 5: sl: コマンドが見つかりません

こんどは実行直後の$?は1だったので、最後にエラーになったgrepコマンドの戻り値がパイプの戻り値になるようです。

ファイルを掴んでいるプロセスを調べる

lsof <ファイル名>でファイルを使用しているプロセルを特定できる。

$ sudo lsof /var/log/messages
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
rsyslogd 1055 root    3w   REG  253,0     7164 100839039 /var/log/messages

実行結果の4番目のフィールドが [0-9]w なのは書き込みモード、[0-9]uが読み書きモードで開かれているファイル。

応用として、書き込みモードで開かれているファイルの一覧取得方法。

$ sudo lsof|awk '$4~/^[0-9][wu][a-zA-Z]*$/{print $O}'

wとuの後には以下のようなロックモードがアルファベト1文字で表示されるので正規表現にその考慮を入れています。

The mode character is followed by one of these lock characters, describing the type of lock applied to the file:

                       N for a Solaris NFS lock of unknown type;
                       r for read lock on part of the file;
                       R for a read lock on the entire file;
                       w for a write lock on part of the file;
                       W for a write lock on the entire file;
                       u for a read and write lock of any length;
                       U for a lock of unknown type;
                       x for an SCO OpenServer Xenix lock on part      of the file;
                       X for an SCO OpenServer Xenix lock on the      entire file;
                       space if there is no lock.

または、/proc/<PID>/fd/にプロセルが使用しているファイルへのシンボリックリンクが貼られているので、ls -lで調べる方法もある。

$ ls -l /proc/5271/fd
total 0
lrwx------. 1 vagrant vagrant 64 Jan  7 15:34 0 -> /dev/pts/0
lrwx------. 1 vagrant vagrant 64 Jan  7 15:34 1 -> /dev/pts/0
lrwx------. 1 vagrant vagrant 64 Jan  7 15:34 2 -> /dev/pts/0
lrwx------. 1 vagrant vagrant 64 Jan  7 15:34 4 -> /home/vagrant/.hoge.txt.swp

CentOS7で試したけど他のOSだと少し違うかも。

awkのNF変数

awkの組み込みのNF変数には現在行のフィールド数が入っています。

$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

$ awk '{print NF}' /etc/redhat-release
5

これを利用して、後方からフィールドを参照できます

$ awk '{print $(NF),$(NF-1)}' /etc/redhat-release
(Core) 7.3.1611

また値をセットすることで現在行のフィールド数を操作できるので、先頭の3フィールド表示するようなことも可能です。

$ awk NF=3 /etc/redhat-release
CentOS Linux release