cut、paste、sort、uniq、head、tail、wcを使ったフィルタコマンド一覧



この記事は2008年~2009年頃に翔泳社で連載していた「業務で楽するためのUNIXテクニック集」の原本です。

はじめに

 この連載では、UNIX系OSをコマンドライン上から効率よく利用するために知っておくべきテクニックや、便利な小技を紹介していきます。

 今回は、「定番のフィルタコマンドを使いこなす」ためのテクニックを紹介します。

スポンサードリンク

関連記事

cut、paste、sort、uniq、head、tail、wcを使った便利なUNIXテクニック例

 コマンドやシェルの制御構造を利用したいくつかの例を紹介しておきます。業務に合わせて応用しながら使ってください。

重複している数の多い順にソートする

$ 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アドレスをソートする

 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アドレスの対応付け一覧が記述されたファイルです。

N行目からM行目を表示する

 ファイル内の特定の区間の行だけを表示したい場合、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行目」のような指定方法で結果を表示しています。なお、シェル関数の詳細な使い方に関しては、次回以降に説明します。

末尾N行を削除して表示する

 次の例は、「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時間、コマンド名などを表示します。

C言語ソースコードのステップ数(行数)をカウントする

 工数の見積もりに、ソースコードの行数を利用する場合に役立ちます。

$ 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の詳細な使い方に関しては、次回以降に説明します。

アクセスログから、アクセス数の多いIPアドレス順に並べる

 アクセス解析の際に役立つと思います。

$ 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のアクセスログ、エラーログが保存されています。

アクセスログから、アクセスしたIPアドレスのホスト名一覧を取得する

$ 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サーバーで名前解決する情報について問い合わせをします。

特定のディレクトリにアクセスした IP アドレス一覧を表示する

 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コマンドの基本文法

$ cut [オプション] [ファイル名]

 主なオプションは次のとおりです。

cutコマンドの主なオプション

オプション 説明
-c 切り出す文字数を指定する
-d 区切り文字を指定する(デフォルトはタブ)
-f 表示する項目を指定。区切った順に左から1,2,3...
-s 列の区切りのない行を無視する

例)「/etc/passwd」ファイルからユーザー一覧を出力

 「/etc/passwd」ファイルは、ログインを許可するユーザー一覧などを管理しています。この中から、ユーザー名だけを出力するには、下記のように指定します。

$ cut -d : -f1 /etc/passwd
root
daemon
...(省略)

例)カンマ区切りの2列目以降を切り出す

$ echo "field1,field2,field3,field4,field5" | cut -d , -f2-
field2,field3,field4,field5

 「,(カンマ)」区切りで、2列目から最後の列まで切り出しています。

例)1文字目と3文字目を切り出す

$ echo "1234567" | cut -c1,3
13

 「-d」オプションで区切り文字を指定しない場合は、タブが使用されます。

例)スペース区切りの1列目と3列目を切り出す

$ 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コマンド

 pasteコマンドは、2つのファイルを行単位で結合します。利用方法は次のようになります。

pasteコマンドの基本文法

$ paste [オプション] [ファイル名]
※注2

FreeBSDなどでは、ファイル名に「-」を指定した場合のみ、標準入力から読み込みます(GNU pasteでは、ファイル名が与えられない場合にも、標準入力から読み込みます)。

 主なオプションは次のとおりです。

pasteコマンドの主なオプション

オプション 説明
-d 区切り文字を指定する(デフォルトはタブ)
-s 1つのファイルの各行を1行に連結できます

例)2つのファイルの内容を結合して出力する

 次のようなファイルに対して、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と同じ働きです)。

 なお、「> ファイル名」と指定することで、指定したコマンドの実行結果をファイルに書き込めます。

例)コマンドの実行結果をfile3に書き込む

$ paste -s -d '\n' file1 file2 > file3

例)lsコマンドの結果を3列で表示する

$ ls | paste - - -
a.txt b.txt c.txt
d.txt e.txt f.txt

ファイル内文字列のソート

sortコマンド

 sortコマンドは、引数で指定されたファイルや、標準入力を並べ替えます。利用方法は次のようになります。

sortコマンドの基本文法

$ sort [オプション] [ファイル名]

 主なオプションは次のとおりです。

sortコマンドの主なオプション
オプション 説明
-b 先頭の空白を無視する
-d 英文字、数字、空白以外の文字を無視する
-k [数値] [数値]番目の列以降をキーとしてソートする
  ([数値]を[数値A,数値B]とすると、[数値A]番目~[数値B]番目の列をキーとしてソートする)
  ([数値]を[列番号.桁数] とすると、ある列の桁数を指定してソートする)
-[数値] [数値]番目の列までをキーとしてソートする
-m 複数のファイルをソートしながらマージする
-n 先頭の文字列を数値として扱う(例:10、101、2 は 2、10、101と並び替える)
-r 結果を降順にする(逆にする)
-t [文字] 列の区切り文字を[文字]で指定した文字にする
-u 重複行を出力しないようにする(「sort | uniq」と同じ)
-z 「\0」(ヌル文字)を区切り文字とする

例)CSV形式のファイルの2列目をソート対象として、昇順で並び替える

$ 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コマンドの基本文法

$ uniq [オプション] [ファイル名]

 主なオプションは次のとおりです。

uniqコマンドの主なオプション

オプション 説明
-c 同一行の数も出力する
-d 重複行のみ表示する
-u 重複のない行のみ表示する

例)ファイルの重複行を1行にまとめ、同一行の数を出力

$ cat test.txt
apple
apple
apple
grape
grape
peach

$ uniq -c test.txt
      3 apple ← appleの文字列がファイル中に3つ存在する
      2 grape
      1 peach

ファイル内の一部分を出力

headコマンド

 headコマンドは、ファイルの先頭部分を表示します。利用方法は次のようになります。

headコマンドの基本文法

$ head [オプション] [ファイル名]

 主なオプションは次のとおりです。

headコマンドの主なオプション

オプション 説明
-[行数] ファイルの先頭から [行数] 分を表示する(-n [行数]も同じ)
-n -[行数] ファイルの末尾の [行数]分以前を表示する
-v ファイル名を表示する

例)1番最近更新されたファイルを表示する

$ ls -t | head -1

 lsコマンドの-tオプションは、最終変更時刻でソートして表示します。デフォルトでは降順で表示されます。

tailコマンド

 tailコマンドは、ファイル内の最後だけ表示します。利用方法は次のようになります。

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コマンドの基本文法

$ wc [オプション] [ファイル名]

 主なオプションは次のとおりです。

wcコマンドの主なオプション

オプション 説明
-c バイト数だけを表示する
-l 行数だけを表示する
-w 単語数だけを表示する

例)サブディレクトリを含むすべてのファイル数を数える

$ ls
a.txt  b.txt  c.txt

$ find . -type f | wc -l
3

 この例では、3個のファイルが存在していることが分かります。

まとめ

 今回は「ファイル内の文字検索・抜き出し・置換」に関するテクニックを紹介しました。また、今回紹介しきれなかった、sedやawk、perlを使えば、より複雑な文字列処理を行えます。次回以降、テーマごとにテクニックを紹介していく中で、順に紹介する予定です。

スポンサードリンク