いますぐ実践! Linuxシステム管理

[バックナンバーのトップへ] [Linux システム管理のトップへ]


いますぐ実践! Linux システム管理 / Vol.197 / 読者数:1758名

こんばんは、うすだです。

ものすごくありきたりですが、今回の東日本大震災により被害にあわれた方々に、 心からお見舞い申し上げます。

わたしは名古屋に生息していますので、当日の地震は、 やけに長い揺れだなあというくらいにしか思いませんでした。

その後、TVなどによる報道をみて、あまりの想像を絶する惨状に、思考が停止して、 呆然とTVの映像を眺めるだけになってしまいました。

ですが、それは、ほぼ影響のない地域にいるから、許されるのですよね。
東北地方や関東地方におられる方々にとっては、現在もなお直面しているわけで、 思考停止なんてもってのほかな状況に置かれていると思います。

自分が恵まれた環境にいるということを意識し、 当たり前だと思っていた日々のことに感謝したいと思います。
そして、時間を無駄に過ごさず、 自分が今できることをきちんとこなして行こうと思います。

というわけですので、いつも通り、メルマガを発行させていただきます。

それでは今回も、はりきってまいりましょうか。

今回のお題 - sed と正規表現でいろいろ自動化する

前回は、正規表現のさわりをご紹介しました。

Vol.196 - いまさらだけど正規表現を始めてみる
http://www.usupi.org/sysad/196.html

正規表現の超基本的なところだけでしたが、雰囲気は感じていただけたと思います。

あとは、使っていくうちに、自力で調べたり試したりすることで、 自然とマスターしていける…のではないかと思います。

ただ、前回の内容だけでは、今後どのようなときに正規表現を使えばよいのか、 ピンとは来なかったかもしれません。

というわけで今回は、正規表現の応用編をお送りしたいと思います。
ただ、grep コマンドばっかりというのもなんですので、sed コマンドを中心に、 正規表現を使った例をいくつかご紹介していきます。

まずは sed コマンドの概要

「sed」コマンドは、テキストデータを行単位で読み込み、変換するためのものです。 ちなみに、「sed」は「stream editor」の略です。

sed がどのように変換を行うかは、スクリプトの形で示します。
スクリプトは、基本的には以下の形式の集まりです。

  アドレス コマンド

「アドレス」は、後述のコマンドを実行する行かどうか、条件を示すためのものです。 省略可能で、省略した場合はすべての行が該当します。
「行番号」だと、指定した行番号の行だけが該当します。
「行番号1,行番号2」だと、行番号1〜2の範囲が該当します。
「/正規表現/」だと、指定した正規表現にマッチする行が該当します。

「コマンド」は、アドレスで指定した行に対して行う処理を指示するためのものです。 よく使う(かもしれない)コマンドを以下に示します。
(後述の例や応用で、使い方をさらっとご説明する予定です。)

コマンド 概要
d 「d」を指定すると、その行を出力しません。
p 「p」を指定すると、その行を出力します。 sed コマンドはデフォルトでは変換しない行もそのまま出力します。ですが、 「-n」オプションをつけて実行すると出力しなくなります。 そんなときこれが役立ちます。
s/文字列/変換後/ 「s」を指定すると、文字列を変換します。
y/文字の羅列/変換後/ 「y」を指定すると、文字列ではなく、1文字ずつ変換します。
q 「q」を指定すると、sed コマンドを終了します。 直後に数値をつけるとそれが終了コードになります。
{ コマンド1; コマンド2 } 複数のコマンドを指定したいときは、 コマンドを「;」でつなげて「{」と「}」で囲みます。

 

また、sed コマンド実行時の、主なオプションなどは以下の通りです。

  sed [-n] [-i[サフィックス]] [-f スクリプトファイル] \
    [-e スクリプト] [入力ファイル]

指定した入力ファイルを1行ずつ読み込み、スクリプトにしたがって処理を行います。 入力ファイルを指定しなかった場合、標準入力から読み込みます。

スクリプトは、「-e」オプションで直接指定するか、 別ファイルに書いておいて「-f」オプションでそのファイルを指定します。
ただ、-e オプションを指定しなくても、通常はスクリプトだと判断してくれます。

「-n」オプションを指定すると、デフォルトでは出力されなくなります。
ピンポイントでその行だけ処理して出力したい、というときに使います。

「-i」オプションを指定すると、結果を標準出力に出力するのではなく、 入力ファイルを直接書き換えます。サフィックスを指定すると、 元の内容を「入力ファイル」+「サフィックス」という名前で残してくれます。

例を示します

一気に説明してしまいました。
なんのことやらさっぱりと思われた大半の貴兄のため、 ここで一気に例を示したいと思います。(一部はその後の応用で説明します。)

まずは、ps コマンドの出力結果の最初の行を消してみましょう。

  $ ps
    PID TTY          TIME CMD
   3420 pts/5    00:00:02 bash
   5097 pts/5    00:00:00 ps

出力結果を次のコマンドなりスクリプトなりに渡して処理する際、 先頭の行はただの説明なので要らない、というケースがあるように思います。
こんなときは、1行目を出力しないようにすればよいので、 スクリプトのアドレスに「1」を、コマンドに「d」(出力しない)を指定します。

  $ ps | sed '1 d'
   3420 pts/5    00:00:02 bash
   5149 pts/5    00:00:00 ps
   5150 pts/5    00:00:00 sed

デフォルトではみんな出力しますので、 指定した1行目だけを出力しないようにできました。
(その代わり、一覧に sed が増えていますが…気にしないように!)

 

同様に、netstat コマンドでもやってみましょう。
経路情報を処理したいけど、最初の説明な行が余計だとします。

  $ netstat -nr
  Kernel IP routing table
  Destination    Gateway       Genmask       Flags MSS Window irtt Iface
  172.16.129.0   0.0.0.0       255.255.255.0 U       0 0         0 vmnet1
  192.168.1.0    0.0.0.0       255.255.255.0 U       0 0         0 eth0
  172.16.215.0   0.0.0.0       255.255.255.0 U       0 0         0 vmnet8
  192.168.122.0  0.0.0.0       255.255.255.0 U       0 0         0 virbr0
  169.254.0.0    0.0.0.0       255.255.0.0   U       0 0         0 eth0
  0.0.0.0        192.168.1.254 0.0.0.0       UG      0 0         0 eth0

今度は、1〜2行目を消し去りたいので、アドレスに「1,2」を指定すればよさそうです。 …もとい、よいです。

  $ netstat -nr | sed '1,2 d'
  172.16.129.0   0.0.0.0       255.255.255.0 U       0 0         0 vmnet1
  192.168.1.0    0.0.0.0       255.255.255.0 U       0 0         0 eth0
  172.16.215.0   0.0.0.0       255.255.255.0 U       0 0         0 vmnet8
  192.168.122.0  0.0.0.0       255.255.255.0 U       0 0         0 virbr0
  169.254.0.0    0.0.0.0       255.255.0.0   U       0 0         0 eth0
  0.0.0.0        192.168.1.254 0.0.0.0       UG      0 0         0 eth0

あるいは、先頭の文字が数字だったら出力する、にしてもよさそうです。
アドレスに「/^[0-9]/」を、コマンドに「p」を指定して、 デフォルトで出力しないよう -n オプションをつけて実行します。

  $ netstat -nr | sed -n '/^[0-9]/ p'
  ...出力結果は同様です...

あるいは、以下のように、先頭が数字なら出力し、それ以外はすべて出力しない、 というスクリプトにしても、同じ結果が得られます。

  $ netstat -nr | sed '/^[0-9]/ p; d'
  ...出力結果は同様です...

コマンドラインでスクリプトを複数指定するときは  上記のように「;」でつなげればよいです。

スクリプトファイルなら、以下のように1行に1つの指令を書けます。

  $ echo > netstat.sed << E-O-F
  /^[0-9]/ p
  d
  E-O-F
  $ netstat -nr | sed -f netstat.sed
  ...出力結果は同様です...

 

行単位での選別だけでなく、中身も書き換えてみましょう。
たとえば、/etc/passwd の「:」をすべて「,」にするには、以下のように実行します。

  $ sed 'y/:/,/' /etc/passwd
  root,x,0,0,root,/root,/bin/bash
  daemon,x,1,1,daemon,/usr/sbin,/bin/sh
  bin,x,2,2,bin,/bin,/bin/sh
  ...

上記は1文字だけの変換ですので、tr コマンドでも実現可能です。

  $ tr : , < /etc/passwd
  ...出力結果は同様です...

もちろん、sed コマンドなら複数の文字を変換できます。
以下は、アルファベットを逆順にして大文字に変換しています。
(どんな効用があるんだよ、と突っ込まれそうですが…あ、暗号?)

  $ sed 'y/abcdefghijklmnopqrstuvwxyz/ZYXWVUTSRQPONMLKJIHGFEDCBA/' \
  /etc/passwd
  ILLG:C:0:0:ILLG:/ILLG:/YRM/YZHS
  WZVNLM:C:1:1:WZVNLM:/FHI/HYRM:/YRM/HS
  YRM:C:2:2:YRM:/YRM:/YRM/HS
  ...

その他の変換については、以降の応用例でご紹介します。

実際に使いそうな応用例

それでは、さらに例を示します。今度は応用的な例です。

たとえば、/etc/ssh/sshd_config の「PermitRootLogin」の設定を、 sed で書き換えたいとしましょう。

そんなときは、「s」コマンドを使用します。
以下では、 PermitRootLogin で始まる行全体を「PermitRootLogin no」に書き換えています。

  # sed 's/^PermitRootLogin.*$/PermitRootLogin no/' /etc/ssh/sshd_config
  ...
  PermitRootLogin no
  ...

ただ、これだと標準出力に出力されるだけです。
ファイルを直接書き換えるには、-i オプションを使用します。

  # grep '^PermitRootLogin' /etc/ssh/sshd_config
  PermitRootLogin yes
  # sed -i 's/^PermitRootLogin.*$/PermitRootLogin no/' \
  /etc/ssh/sshd_config
  # grep '^PermitRootLogin' /etc/ssh/sshd_config
  PermitRootLogin no

sed コマンドの実行前と実行後で、書き換わっているのがわかります。

元の内容を残したいときは、-i オプションの直後にサフィックスを指定します。 すると、ファイル名+サフィックス という名前で残ります。
たとえば、サフィックスに「.20110320」を指定してみましょう。
(先ほど no に書き換えましたので、今度は yes に変更しています。)

  # sed -i.20110320 's/^PermitRootLogin.*$/PermitRootLogin yes/' \
  /etc/ssh/sshd_config
  # ls -1 /etc/ssh/sshd_config*
  /etc/ssh/sshd_config
  /etc/ssh/sshd_config.20110320
  # diff /etc/ssh/sshd_config.20110320 /etc/ssh/sshd_config
  28c28
  < PermitRootLogin no
  ---
  > PermitRootLogin yes
  (個人的には diff -u の出力の方が好きです)

/etc/ssh/sshd_config.20110320 というファイルが作成されていることが (diff コマンドの出力結果などから)わかります。

これで、たとえば、ある時間帯だけ PermitRootLogin を許可するというようなことが、 (cron を利用するなどすれば)できるようになります。

 

もうひとつ、ファイル名をまとめて変換するという、先ほどとは少し違う例を示します。

たとえば、「数字.txt」というファイル名をたくさん作ったとします。
(以下を実行すると、そんな状況を作り出せます。)

  $ for num in `seq 1 19`; do
  > touch ${num}.txt
  > done
  $ ls -1
  1.txt
  10.txt
  11.txt
  ...
  8.txt
  9.txt

ここで、他と区別できるよう、 これらを「test数字.txt」というファイル名に変更する必要が生じた (しかも数字は3桁)としましょう。

文字列の変換には「s」コマンドを使えばよいですが、元の数字も必要になりますよね。 そんなときは、正規表現の中で、変換後に引用したい部分を「\(」と「\)」で囲みます。 そして、変換後の文字列に「\1」と書いておくと、 その部分が引用された文字列に置き換わります。

…文章で説明すると分かりにくいですので、 実際に数字の部分だけを抜き出してみましょう。

  $ echo 1.txt | sed 's/^\([0-9]*\)\.txt/\1/'
  1
  $ echo 19.txt | sed 's/^\([0-9]*\)\.txt/\1/'
  19

数字を表す「[0-9]*」を「\(」と「\)」で囲み、「\1」だけにしていますので、 数字だけが出力されるという寸法です。

あとは、実際にファイル名を変換するだけですね。
以下では、一旦 num という変数に数字を入れておき、 mv コマンドで改名する際に使用しています。

  $ for file in *.txt; do
  > num=`echo $file | sed 's/^\([0-9]*\)\.txt/\1/'`
  > mv $file `printf "test%03d.txt" $num`
  > done
  $ ls
  test001.txt  test005.txt  test009.txt  test013.txt  test017.txt
  test002.txt  test006.txt  test010.txt  test014.txt  test018.txt
  test003.txt  test007.txt  test011.txt  test015.txt  test019.txt
  test004.txt  test008.txt  test012.txt  test016.txt

いきなり mv するのは勇気が要りますので、まずはファイル名を出力するだけにして、 正しいかどうか確認したほうがよいと思います。

  $ for file in *.txt; do
  > num=`echo $file | sed 's/^\([0-9]*\)\.txt/\1/'`
  > echo mv $file `printf "test%03d.txt" $num`
  > done
  mv 1.txt test001.txt
  mv 10.txt test010.txt
  ...

複数引用したいときは、引用したい箇所すべてを「\(」と「\)」で囲み、 「\数字」で引用します。(数字は、囲んだ順番です。)
たとえば、以下では、/etc/passwd のユーザ名とUIDを引用しています。

  $ sed 's/^\([^:]*\):[^:]*:\([0-9]*\):.*$/\1 = \2/' /etc/passwd
  root = 0
  daemon = 1
  bin = 2
  ...

おわりに

以上、sed コマンドの概要と、sed コマンドを利用した応用例をいくつかご紹介しました。

sed コマンドをうまく使うことで、 いろんな変更をスクリプトの形にして自動化することができるようになります。

いろいろ試したり、先駆者(?)が残したスクリプトを読むなどして、 己れの技術を磨いてくださいませ。

宿題の答え

前回の宿題は、

  httpd.conf から有効な「DocumentRoot」だけを抜き出しましょう。

でした。

読者のみなさまがどのような「httpd.conf」をお持ちなのかわかりませんが、 とりあえず、手元にある httpd.conf で試してみたいと思います。

まずは、単純に grep してみるところから始めてみましょう。

  $ grep DocumentRoot /etc/httpd/httpd.conf
  # DocumentRoot /var/www
  DocumentRoot /home/www/htdocs
     DocumentRoot /work/www
        DocumentRoot /work/www2
  Alias /cgi-test/ "/work/www/DocumentRoot/cgi/"
  <Directory "/work/www/DocumentRoot/cgi">

ここで有効な結果だけが得られたのなら、それでめでたくおしまいになるのですが、 幸い、いくつか関係ないものも出力されています。

さて、上記の中で、「DocumentRoot」ディレクティブとして有効なのは、 2〜4行目だけだと思われます。

  DocumentRoot /home/www/htdocs
     DocumentRoot /work/www
        DocumentRoot /work/www2

ですので、以降では、残りの1行目と5〜6行目を取り除いてみます。

まず、DocumentRoot の前に余計なものが入っているといかんかなということで、 先頭が DocumentRoot で始まるものを抜き出してみます。

  $ grep '^DocumentRoot' /etc/httpd/httpd.conf
  DocumentRoot /home/www/htdocs

当然、これだと2行目だけしか得られません。
3〜4行目はスペースでインデントされているようですので、 DocumentRootの手前にスペースがある(もしくはなにもない)行を抜き出してみます。

  $ grep '^ *DocumentRoot' /etc/httpd/httpd.conf
  DocumentRoot /home/www/htdocs
     DocumentRoot /work/www

2〜3行目が得られました。ただ、4行目が出てきません。
どうやら、タブでインデントされているようです。
コマンドラインでは、Ctrl+v の後にタブを押すと、 タブを直接入力することができます。
(「(ここにタブ)」のところで、Ctrl+v と TAB を押してください。)

  $ grep '^[ (ここにタブ)]*DocumentRoot' /etc/httpd/httpd.conf
  DocumentRoot /home/www/htdocs
     DocumentRoot /work/www
        DocumentRoot /work/www2

これで、2〜4行目がすべて得られました。
ただ、なんだか釈然としませんよね。
実は、GNU の grep コマンドは、 空白文字を「[:space:]」という記述で表すことができます。ですので、 上記は以下のように表せます。

  $ grep '^[[:space:]]*DocumentRoot' /etc/httpd/httpd.conf
  DocumentRoot /home/www/htdocs
     DocumentRoot /work/www
        DocumentRoot /work/www2

これなら、スクリプトなどの中でも安心して使えそうです。

ちなみに、「[:alpha:]」や「[:xdigit:]」などもあります。
詳しくはオンラインマニュアル(man grep)などをご覧くださいませ。

今回の宿題

今回の宿題は、

  ディスクフルのとき、-i オプションで sed を実行すると、どうなるか
  試してみましょう。

です。

自分でスクリプトを書いてシステムに仕込んだりすると、 正常でないときにもちゃんと処理されるかどうか、ということが気がかりになります。

最近は、ディスクが不足することはあまりないようにも思いますが、 それでもうっかり溢れさせるということがないとも限りません。

そんなとき、設定ファイルを sed コマンドで書き換えたら、 書き換えたものも元のファイルも消えてしまった、ということは避けたいですよね。

ディスクフル時の -i オプションの挙動を確認しておくべきだということで、 宿題にさせていただこうと思います。

あとがき

日本人被災者のマナーのよさや冷静さが、世界で注目を浴びているという話を、 ネット上で頻繁に見かけます。

阪神淡路大震災や、他の震災のときにも、同様に注目されていたように思いますが、 今回は特に注目されているようです。

そして、その理由のひとつがインターネットではないかという話が、各所でみられます。

実際、節電を呼びかける「ヤシマ作戦」や、 不要な買いだめをやめて譲り合う「ウエシマ作戦」など、 インターネットを起点として広まる活動が、たくさんあるように思われます。

ヤシマ作戦とは - はてなキーワード
http://d.hatena.ne.jp/keyword/%A5%E4%A5%B7%A5%DE%BA%EE%C0%EF
ウエシマ作戦とは - はてなキーワード
http://d.hatena.ne.jp/keyword/%A5%A6%A5%A8%A5%B7%A5%DE%BA%EE%C0%EF

すばらしいことですね。しかも、それらが自主的に行われているというのが、 すごいことだと思います。

今後、日本がどのような道を歩んでいくのか、わたしにはさっぱりわかりません。 ですが、日本人のこのような点をみる限り、悲観する必要はないのではないかと思います。

というわけで、これからは、日本人であることを誇りに思い、 胸を張って生きていけるような行動を行っていきたいと思います。

 

さて、冒頭で、自分が今できることをこなしますとか、 いつも通りに発行していきますとか書きました。

そんなことを言った矢先に大変申し訳ありませんが、諸般の事情により、 現在非常に切羽詰まっておりますため、 次回(予定では4月3日)をお休みとさせていただきたいと存じます。

理由のうちもっとも大きなものに関しては、 目処がついた時点でお知らせすることができると思います。 (期待せずに待っていてくださいませ。)

というわけで、申し訳ありませんが、よろしくお願いいたします。

 

今回も、ここまで読んでいただき、たいへんありがとうございました。
次回は、4月17日(日) の未明にお会いしましょう!

 

「いますぐ実践! Linux システム管理」の解除は、以下からできます。
http://www.usupi.org/sysad/ (まぐまぐ ID:149633)

バックナンバーは、こちらにほぼ全部そろっています。
http://www.usupi.org/sysad/backno.html

「栗日記」- 東北方面の読者の方々が心配ですが、毎日描いております。
http://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
http://usupi.org/k/ (モバイル栗日記)


[バックナンバーのトップへ] [Linux システム管理のトップへ]

トップ

バックナンバー
    [日付順] [目的別]

プロフィール

▼ リンク

独学Linux
Linuxデスクトップ環境に関する情報が満載です。 メルマガもありますよ。
Server World
CentOS 6をサーバとしたときの設定例が、これでもかというくらいたくさん載っています。 CentOS以外のディストリビューション(Fedora, Ubuntu)も充実しています。
LINUXで自宅サーバーを構築・導入(Fedora9)
Fedora9のインストールの仕方から管理方法まで、詳しく載っています。 SearchManには情報がもりだくさんです。
マロンくん.NET
〜サーバ管理者への道〜
Linuxをサーバとして使用するための、いろいろな設定方法が載っています。 マロンくんもかわいいです。 なんといっても、マロンくんという名前がいいですね!!
日経Linux
今や数少なくなってしまったLinuxの雑誌。ニュースやガイドもあります。
Linux Square − @IT
@ITが提供する、Linux の情報が満載。 載っていない設定方法はないんじゃないでしょうか。
gihyo.jp…技術評論社
Linuxに限らず様々な技術情報が満載のサイト。 SoftwareDesign誌も、 ソフトウェア技術者は必見です。
SourceForge.JP Magazine
Linux に限らず、オープンソース関連の記事が網羅されています。
ITmediaエンタープライズ:Linux Tips 一覧
Tips というより FAQ 集でしょうか。わからないことがあれば覗きましょう。
IBM developerWorks : Linux
開発者向けですが、勉強になりますよ。
Yahoo!ニュース - Linux
Yahoo!のLinuxに関するニュース一覧です。
栗日記
システム管理とかと全然関係ありませんが、毎日栗の絵を描いています。
システム管理につかれちゃったとき、癒されたいときに、ご覧ください。:-)
WEB RANKING - PC関連
ランキングに参加してみました。押してやってください。

▼ 作ってみました

Add to Google

▼ せんでん




▼ 最近読んだ本

▼ 気に入ってる本