この記事は2008年~2009年頃に翔泳社で連載していた「業務で楽するためのUNIXテクニック集」の原本です。
この連載では、UNIX系OSをコマンドライン上から効率よく利用するために知っておくべきテクニックや、便利な小技を紹介していきます。
今回は、「定番のフィルタコマンドを使いこなす」ためのテクニックを紹介します。
スポンサードリンク
コマンドやシェルの制御構造を利用したいくつかの例を紹介しておきます。業務に合わせて応用しながら使ってください。
$ cat foo.txt | sort | uniq -c | sort -k1 -n -r 4 aaa 2 bbb 1 ccc
sortコマンドを2回使っています。
foo.txt に日本語が含まれている場合は、以下のように LC_ALL=C という環境変数を設定してソートしましょう。
$ cat foo.txt | LC_ALL=C sort | LC_ALL=C uniq -c | sort -k1 -n -r
「bin」という名前のディレクトリを「:(コロン)」で区切ってリストにします。PATH環境変数を生成するのに役立ちます。
$ find / -name "bin" -type d | paste -s -d : -
ワイルドカードを利用することで、隠しファイルを含んだファイル・ディレクトリのサイズを昇順で出力しています。
$ du -cs * .[^\.]* | sort -n 4 .bashrc 8 .bash_profile 12 .emacs 1164 backup 1584 hoge 2772 total
「* .[^\.]*」は、任意の複数文字のファイルと、「.(ドット)」から始まる任意の複数文字のファイル名に展開されます。
任意の長さの乱数を得たい場合に役に立ちます。次の例では6文字のランダムな文字列を出力しています。
$ strings /dev/urandom | grep -o [0-9a-zA-Z] | head -n 6 | paste -d '\0' -s -; echo ytiO0k
「/dev/urandom」は、乱数を生成する仮想デバイスです。stringsコマンドは、ファイル中の表示可能な文字列を表示します。
※注4 GNU strings 2.16(2005年)以前を使用すると、下記のような警告が出て正しい結果が得られないので、注意してください。 strings: Warning: '/dev/urandom' is not an ordinary file
IPアドレスを、「.(ドット)」で区切られた各組でソートした結果を出力します。
$ sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 /etc/hosts 127.0.0.1 localhost.localdomain localhost hoge 192.168.0.30 hoge.hoge.com
例えば「sort -n -k 1,1」と指定すると、1列目を数値として順に並べ替えます。「/etc/hosts」は、ホスト名とIPアドレスの対応付け一覧が記述されたファイルです。
ファイル内の特定の区間の行だけを表示したい場合、headコマンドとtailコマンドを利用することができます。例えば、3行目~7行目だけを表示するには、次のように指定します。
$ seq 1 10 | head -7 | tail -n +3 3 4 5 6 7
単調増加(減少)する数値列を表示するseqコマンドで1~10の数値列を出力し、headコマンドとtailコマンドで表示行を限定しています。
また、シェル関数を定義すると、次のようにも指定できます。
$ body () { N=$1; M=$2; tail -n +$1 | head -$((M-N+1)); } $ seq 1 10 | body 3 7 3 4 5 6 7
上の例では、「body N行目 M行目」のような指定方法で結果を表示しています。なお、シェル関数の詳細な使い方に関しては、次回以降に説明します。
次の例は、「1~10」の数値列から末尾5行(6~10)を削除して表示しています。
$ seq 1 10 | tail -r | tail +6 | tail -r 1 2 3 4 5
tailコマンドの「-r」オプションを2回利用しています。GNU tailのように「-r」オプションが存在しない環境では、次のよう指定できます。
$ seq 1 10 | tac | tail -n +6 | tac
tacコマンド(catコマンドの逆)は、ファイルの内容を行単位で逆に表示します。
$ ps aux | sort -n -k 4 | tail .... root 21269 0.0 1.0 9144 2728 ? Ss 09:37 0:00 sshd: root@notty root 11442 0.0 3.9 80108 10184 ? Sl Mar17 2:50 gnome-terminal root 346 0.1 8.7 28552 22300 pts/3 S Mar19 7:08 emacs
psコマンドは、プロセス識別子、端末名、CPU時間、コマンド名などを表示します。
工数の見積もりに、ソースコードの行数を利用する場合に役立ちます。
$ find . \( -name "*.h" -o -name "*.c" \) -print0 | xargs -0 cat | wc -l 10311
例は、ヘッダファイル(.h)・ソースファイル(.c)合わせて10311行で構成されていることを意味しています。
なお、空白行を除いてカウントしたい場合は、次のように指定できます。
$ find . \( -name "*.h" -o -name "*.c" \) -print0 | xargs -0 grep . | wc -l 8582
findコマンド、xargsコマンドのオプションの詳細は第1回を参照ください。
$ history | awk '{print $2}' | sort | uniq -c | sort -nr | head 112 ls 55 for 36 :> 29 rm 24 find
historyコマンドで得られるコマンド履歴を、整形して多用順に並び替えています。この例では、lsコマンドを112回利用している事が分かります。awkの詳細な使い方に関しては、次回以降に説明します。
アクセス解析の際に役立つと思います。
$ cut -d ' ' -f1 access.log | sort | uniq -c | sort -nr | head -10 | nl 1 34375 210.XXX.86.4 2 20005 137.XXX.0.41 3 18489 137.XXX.0.42 4 17538 219.XXX.244.93 5 11252 222.XXX.237.24 ....
nlコマンドは、行番号を付与します。
なお、Fedoraでは「/var/log/httpd/」以下に、Debianでは「/var/log/apache/」以下に、「access.log」「error.log」というApacheのアクセスログ、エラーログが保存されています。
$ for i in $(cut -d ' ' -f1 access.log | sort -u); { host $i; } 169.66.249.66.in-addr.arpa domain name pointer crawl-66-249-66-169.googlebot.com. 114.67.249.66.in-addr.arpa domain name pointer crawl-66-249-67-114.googlebot.com. .....
hostコマンドは、DNSサーバーで名前解決する情報について問い合わせをします。
gz形式で圧縮されたApacheログ(access.log.1.gz、access.log.2.gz・・・)から、「/blog」ディレクトリにアクセスしたIPアドレスを表示しています。
$ zcat access.log.*.gz | grep "/blog" | cut -d ' ' -f1 | sort -u 111.XX.9.82 122.XX.140.216 124.XXX.200.162 125.XXX.231.38 .....
この例では、zcatコマンドを利用して、圧縮されたファイル(拡張子.gz)の内容を表示しています。
標準入力からデータを受け取り、その結果を標準出力に出力するコマンドを「フィルタコマンド」と呼びます。フィルタコマンドは、「|(パイプ)」で繋げることで、ログの解析などさまざまな処理ができます。
定番のフィルタコマンド(cut、paste、sort、uniq、head、tail、wc)のテクニックを説明します。
cutコマンドは、特定の文字で区切られた項目を分割します。利用方法は次のようになります。
$ cut [オプション] [ファイル名]
主なオプションは次のとおりです。
cutコマンドの主なオプション
オプション | 説明 |
---|---|
-c | 切り出す文字数を指定する |
-d | 区切り文字を指定する(デフォルトはタブ) |
-f | 表示する項目を指定。区切った順に左から1,2,3... |
-s | 列の区切りのない行を無視する |
「/etc/passwd」ファイルは、ログインを許可するユーザー一覧などを管理しています。この中から、ユーザー名だけを出力するには、下記のように指定します。
$ cut -d : -f1 /etc/passwd root daemon ...(省略)
$ echo "field1,field2,field3,field4,field5" | cut -d , -f2- field2,field3,field4,field5
「,(カンマ)」区切りで、2列目から最後の列まで切り出しています。
$ echo "1234567" | cut -c1,3 13
「-d」オプションで区切り文字を指定しない場合は、タブが使用されます。
$ echo "field1 field2 field3 field4 field5" | cut -d ' ' -f1,3 field1 field3
「 (空白)」区切りの場合は、「' '(シングルクォーテーション)」、もしくは「" "(ダブルクォーテーション)」で括ります。
※注1 cutコマンドでは、連続する空白文字を1つの区切り文字として扱うことができません。空白区切りの列を切り出すにはawk、sedなどが利用できます。 awkを使うと次のように指定できます(詳細は次回以降に説明します)。 $ echo "field1 field2 field3 field4 field5" | awk '{ print $1,$3 }'
pasteコマンドは、2つのファイルを行単位で結合します。利用方法は次のようになります。
$ paste [オプション] [ファイル名]
※注2 FreeBSDなどでは、ファイル名に「-」を指定した場合のみ、標準入力から読み込みます(GNU pasteでは、ファイル名が与えられない場合にも、標準入力から読み込みます)。
主なオプションは次のとおりです。
pasteコマンドの主なオプション
オプション | 説明 |
---|---|
-d | 区切り文字を指定する(デフォルトはタブ) |
-s | 1つのファイルの各行を1行に連結できます |
次のようなファイルに対して、pasteコマンドを適用してみます。
file1の内容
$ cat file1 3, 8 5, 12 1, 3
file2の内容
$ cat file2 30, 2 4, 8 5, 9
2つのファイルを行単位で結合する場合
$ paste file1 file2 3, 8 30, 2 5, 12 4, 8 1, 3 5, 9
2つのファイルの内容を順番に出力する場合
$ paste -s -d '\n' file1 file2 3, 8 5, 12 1, 3 30, 2 4, 8 5, 9
「-s」オプションで1行に連結、「-d '\n'」オプションにより改行を区切り文字にすることで、ファイルの内容を順番に出力しています(cat file1 file2と同じ働きです)。
なお、「> ファイル名」と指定することで、指定したコマンドの実行結果をファイルに書き込めます。
$ paste -s -d '\n' file1 file2 > file3
$ ls | paste - - - a.txt b.txt c.txt d.txt e.txt f.txt
sortコマンドは、引数で指定されたファイルや、標準入力を並べ替えます。利用方法は次のようになります。
$ sort [オプション] [ファイル名]
主なオプションは次のとおりです。
オプション | 説明 |
---|---|
-b | 先頭の空白を無視する |
-d | 英文字、数字、空白以外の文字を無視する |
-k [数値] | [数値]番目の列以降をキーとしてソートする |
([数値]を[数値A,数値B]とすると、[数値A]番目~[数値B]番目の列をキーとしてソートする) | |
([数値]を[列番号.桁数] とすると、ある列の桁数を指定してソートする) | |
-[数値] | [数値]番目の列までをキーとしてソートする |
-m | 複数のファイルをソートしながらマージする |
-n | 先頭の文字列を数値として扱う(例:10、101、2 は 2、10、101と並び替える) |
-r | 結果を降順にする(逆にする) |
-t [文字] | 列の区切り文字を[文字]で指定した文字にする |
-u | 重複行を出力しないようにする(「sort | uniq」と同じ) |
-z | 「\0」(ヌル文字)を区切り文字とする |
$ cat test.csv 1,30,5,6 3,4,5,5 2,6,3,2 $ sort -t , -n -k 2 test.csv 3,4,5,5 2,6,3,2 1,30,5,6
「,(カンマ)」を区切り文字として、文字列を数値として扱うことで並び替えています。
※注3 POSIXのバージョン(POSIX 1003.1-2001(200112:2001年12月以降))によっては、「-k 数値」オプションを「+数値」と指定するとWarningが出力されます。 $ sort -t, -n +1 test.csv sort: Warning: "+number" syntax is deprecated, please use "-k number" ここで、「+1」が、2番目とそれ以降の列をもとにソートするという意味になります。この場合、次のように古いバージョンのPOSIXを指定して回避するか、「-k」オプションを利用してください。 $ export POSIX2_VERSION=199209 $ sort -t, -n +1 test.csv なお、head -[数値]、またはtail +[数値]なども同様です。
uniqコマンドは、テキストファイル内の重複行を確認できます。利用方法は次のようになります。
$ uniq [オプション] [ファイル名]
主なオプションは次のとおりです。
uniqコマンドの主なオプション
オプション | 説明 |
---|---|
-c | 同一行の数も出力する |
-d | 重複行のみ表示する |
-u | 重複のない行のみ表示する |
$ cat test.txt apple apple apple grape grape peach $ uniq -c test.txt 3 apple ← appleの文字列がファイル中に3つ存在する 2 grape 1 peach
headコマンドは、ファイルの先頭部分を表示します。利用方法は次のようになります。
$ head [オプション] [ファイル名]
主なオプションは次のとおりです。
headコマンドの主なオプション
オプション | 説明 |
---|---|
-[行数] | ファイルの先頭から [行数] 分を表示する(-n [行数]も同じ) |
-n -[行数] | ファイルの末尾の [行数]分以前を表示する |
-v | ファイル名を表示する |
$ ls -t | head -1
lsコマンドの-tオプションは、最終変更時刻でソートして表示します。デフォルトでは降順で表示されます。
tailコマンドは、ファイル内の最後だけ表示します。利用方法は次のようになります。
$ tail [オプション] [ファイル名]
主なオプションは次のとおりです。
tailコマンドの主なオプション
オプション | 説明 |
---|---|
-[行数] | ファイルの末尾から[行数]分を表示する(-n [行数]も同じ) |
-n +[行数] | ファイルの先頭から[行数]分以降を表示する |
-f | ファイルの末尾まで読み込んでも終了しないで読み続ける |
-F | ファイル名の変更などが発生した場合、新ファイルをオープンしなおす |
-r | ファイルの内容を行単位で逆にする(GNU tail には存在しない) |
-v | ファイル名を表示する |
$ tail -f /var/log/messages Mar 22 23:17:01 localhost CRON[1688]: (pam_unix) session closed for user root Mar 22 23:39:01 localhost CRON[1691]: (pam_unix) session opened for user root by (uid=0) ....
「-f」オプションは、ファイルに追加された内容をリアルタイムに表示します。「/var/log/messages」には、さまざまなプログラムのログが出力されます。このため、ログファイルを監視する際に役立ちます。
なお、監視中のtailコマンドを停止させるには、[CTRL+C]を押下します。
wcコマンドは、テキスト・ファイルの行数、単語数、バイト数を標準出力に渡します。利用方法は次のようになります。
$ wc [オプション] [ファイル名]
主なオプションは次のとおりです。
wcコマンドの主なオプション
オプション | 説明 |
---|---|
-c | バイト数だけを表示する |
-l | 行数だけを表示する |
-w | 単語数だけを表示する |
$ ls a.txt b.txt c.txt $ find . -type f | wc -l 3
この例では、3個のファイルが存在していることが分かります。
今回は「ファイル内の文字検索・抜き出し・置換」に関するテクニックを紹介しました。また、今回紹介しきれなかった、sedやawk、perlを使えば、より複雑な文字列処理を行えます。次回以降、テーマごとにテクニックを紹介していく中で、順に紹介する予定です。
スポンサードリンク