CLIマジック:使って役立つワンライナー入門

 通常この分野におけるワンライナー(one-liner)とは、パイプ(|)という機能を用いて複数のコマンドを1行にまとめたものを意味する。パイプの機能は、その左側に記述されたコマンドの実行結果を、右側に記述されたコマンドの入力として渡すことである。本稿で解説するのはbashコマンドプロンプトでの使用例だが、簡単な構成のワンライナーにせよ複雑な構成のワンライナーにせよ、これらはいずれも使い方次第で大いに役立つ存在なのだ。

 例えば、カレントのディレクトリに何個のファイルが置かれているかを確認するには、下記のコマンドを実行すればいい。

ls | wc -l

 これは最も簡単な部類に属すワンライナーのサンプルであって、状況によってはより複雑な記述をすることになる。例えば、手元のシステムにて実行中のプロセスのうちでCPU時間を最大限に消費しているもの上位5つを特定するには、下記のコマンドを実行すればいい。

ps -eo user,pcpu,pid,cmd | sort -r -k2 | head -6

 このうちpsコマンドの次にあるoは、表示対象とする列の指定用オプションである。その次のsort -rではk2という指定により、2行目の列(pcpu)を基にした逆順のソートを施させている。その次のheadで行っているのは、ヘッダ行を含めた先頭6行のみを先のソート結果から取得するための指定である。なおsortはデフォルトで先頭列をソート対象とするので、pcpuの指定位置を1番目とするようサンプルコードの記述を変更するとk2オプションは省略できる。つまり同じ処理をするワンライナーであっても、その記述法は複数存在するのであり、逆に記述法や用いるオプションを変更することで、異なる実行結果が得られることもある。

 複数のユーザが利用するLinuxサーバを管理する場合、整理されたユーザのリストを手早く入手できると便利なはずだ。下記のコマンドもそうした用途で用いるワンライナーの1つである。

cat /etc/passwd | sort

 ただしユーザ名だけが必要という場合、この指定では余分な情報も多数出力されてしまう。その点を改良したのが下記のワンライナーだ。

cat /etc/passwd | sort | cut -d":" -f1

 これはソート後の情報をcutにて処理するよう変更したもので、その部分にあるdはフィールド間の区切り記号を指定するためのオプションだ。つまりcutの役割は各行における区切り部にてフィールドを分割することであり、ここでは1番目のフィールドが必要なので、それをf1という指定にて抽出させているのである。これにより“ユーザ名だけが必要”という要望は一応かなえられることになるが、問題はシステムのユーザ名として、apache、bin、lpなども混在してくることだ。これを人間のユーザだけに絞り込むには、下記のような指定をすればいい。

cat /etc/passwd | sort | gawk '$3 >= 500 {print $1 }' FS=":"

 ここで追加したgawkは、パイプ経由で渡された情報を行単位で評価するためのコマンドだ。このサンプルでは、3番目のフィールドであるUID値が500以上の場合に限って(最近のディストリビューションでは、この番号以降のID値が通常のユーザに割り当てられる)後続のアクションを行うという指定が施されている。この場合の後続のアクションとは中カッコで示された部分で、具体的にはユーザ名である1番目のフィールドを表示させるという処理だ。なおgawkコマンドにフィールド区切りと認識させる記号はFSオプションにて指定するようになっており、ここではコロンを指定している。

 次に、拡張子が不揃いなファイルが多数混在するディレクトリにおいて.phpファイルだけを選択し、該当するファイルをそれぞれfilename.bkpという名称にて個別バックアップしたいという場合を考えてみよう。こうしたジョブをワンライナーにて処理するには、下記のように指定すればいい。

for f in *.php; do cp $f $f.bkp; done

 ここで行っているのは、カレントディレクトリ中の全ファイルを調べて、拡張子が.phpであるものだけを取り出すという操作の繰り返しだ。$fは個々のファイル名を保持するための作業用変数であり、最後にcpコマンドによる単純なバックアップを行わせている。なおこのサンプルコードについては、パイプを介した出力の伝達ではなく、セミコロン区切りによってコマンド群を順次実行させている点に注目して頂きたい。

 次にバルクコピーを実施する場合を想定し、下記のようなワンライナーを検討してみよう。

tar cf - . | (cd /usr/backups/; tar xfp -)

 ここではまずカレントディレクトリに対する再帰的なtarパッケージ作成を行い、そうして得られた1つのパッケージを次のコマンドにパイプさせている。次にある丸カッコは一時的なサブシェルを作成するための指定で、その中で行っているのはオリジナルのディレクトリ全体を復元させることを目的とした、ディレクトリの変更およびパッケージの展開である。なおtarコマンドの最後に付けているpオプションは、タイムスタンプやパーミッションなどのファイルプロパティを保持させるための指定だ。またこのワンライナーの実行が終了すると、シェルコンテクストはオリジナルのディレクトリに復帰する。

 下記のワンライナーは、先と同様の処理をリモートサーバに対して施すための指定である。

tar cf - . | ssh smith@remote.server tar xfp - -C /usr/backup/smith

 このコマンドについては、SSHリモートセッションの確立を追加した他、Cオプションを用いてtarパッケージの展開先ディレクトリを/usr/backup/smithに変更させている。

grep、gawk、uniqとの併用

 ワンライナーを使ってよく行われる処理の1つが各種のテキスト操作であり、特にこうした作業の場合、適切なコマンドの組み合わせが驚くべき能力を発揮してくれる。ここでは具体的な例として、下記のような形式で得られる着信メールの一覧を整理するという処理を考えてみよう。

cat incoming_emails
2008-07-01 08:23:17 user1@example.com
2008-07-01 08:25:20 user2@someplace.com
2008-07-01 08:32:41 somebody@server.net
2008-07-01 08:35:03 spam not recived, filtered
2008-07-01 08:39:57 user1@example.com
...

 こうした着信メールの受信者名一覧を作成する場合にまず問題となるのが、catコマンドによる処理だけでは同じ受信者が重複して登場する可能性があることだ。そうした問題も下記のワンライナーによって解決できる。

grep '@' incoming_email | gawk '{print $3}' | sort | uniq

 ここのgrepでは、電子メールのアドレス行には@記号が含まれているという前提で、該当行のフィルタリング処理を行っている。その次のgawkは3番目のフィールドに位置するメールアドレスを抽出するためのもので、その結果は次のsortコマンドに渡されている。このソート処理は同一の受信者名を隣接した行に集めておくための措置であり、そのような重複箇所を最終的に整理させているのが、ソート済みリストにおける重複行を削除するuniqコマンドだ。こうして得られる結果は、下記のようになるはずである。このようにテキスト操作用のワンライナーは、grepsedawkordertrcutuniqなどのコマンドを組み合わせて記述されるケースが多い。

somebody@server.net
user1@example.com
user2@someplace.com

 こうしたワンライナーが便利だとしても、必要時に入力するには長すぎるというのが正直なところだろう。そうした不満を解消するには、事前に下記のようなコマンドエイリアスを作成して各自の.bashrcファイルに組み込んでおけばいい。このファイルに登録しておいたものはセッションへのログイン時に自動実行されるため、個人用に作成しておいたエイリアスであっても、即座に利用可能となるはずである。

alias p5="ps -eo pcpu,user,pid,cmd | sort -r | head -6"

 本稿で紹介したワンライナーはいずれも入門用のサンプルであり、これらを簡単かつ高機能化したバリエーションはいくらでも作成できるはずだ。特にLinuxシステムの管理者の場合、この種のワンライナーがいつ必要になってもおかしくないので、常日頃から役立つワンライナーの作成と収集およびそれらの改良を心がけておくべきだろう。また“自分はこんな便利なワンライナーを作成した”という読者がおられたら、積極的に他のユーザにも紹介して頂きたい。

Sergio Gonzalez Duranは、Linux管理者、システム開発者、ネットワークセキュリティカウンセラとして活動する傍ら、Linux関連の講座を担当し、またスペイン語ベースのLinux/オープンソースWebサイト(linuxtotal.com.mx)も運営している。

Linux.com 原文(2008年7月23日)