名前付きパイプを試してみる

Unix/Linux には 名前付きパイプというものがあります。

Unixドメインソケットの説明で「名前付きパイプに類似した機能を備え〜」と書いてあって 今まで何となく「パイプに名前付けらるんだな」くらいにと思ってましたが 実際に使ったことがなかったので、試してみます。

使用するのは mkfifo というコマンド。

$ mkfifo /tmp/namedpipe

これで作成完了。

$ ls -l /tmp/namedpipe

prw-rw-r-- 1 xxxxx xxxxx 0 Oct 22 22:06 /tmp/namedpipe

なんかできてますし 権限の先頭が "p" になってます。

機能はなんとなく想像できますね。

動作を確認するために、 まず別のコンソールで この名前付きパイプを tail します。

$ tail -f /tmp/namedpipe

待機状態になりました。

tail しているコンソールとは別のコンソールで 名前付きパイプに向かってリダイレクトしてみます。

$ echo test1 > /tmp/namedpipe
$ echo test2 > /tmp/namedpipe

tail しているコンソール側に 出力されていきます。

$ tail -f /tmp/namedpipe

test1
test2

なるほど、直接つながっていなくても 名前付きパイプを通してデータが連携できてますね。

rm コマンドで削除できます。

$ rm /tmp/namedpipe

ここまでだと、ほぼ通常のファイルを使っても同じようなことができますが 受け側で tail していない状態でリダイレクトすると 通常のファイルとは違う動きになりました。

$ echo test3 > /tmp/namedpipe

受け側が 名前付きパイプを tail していないとこの状態で リダイレクトすると リダイレクトした側が待機状態になります。

受け側で 名前付きパイプに tail や cat などすると リダイレクト側の待機が解除されます。

$ cat /tmp/namedpipe

test3

便利に使えるケースはありそうですね。

bash の 非インタラクティブモード では alias は実行できない

ちょっとハマりかけたのでメモ。

bash で 「alias を使ったら便利!」と 思った処理があったので 使おうと思ったんですが使えませんでした。

[参考]
【Bash】 スクリプト中のaliasコマンドが実行できない - takafumi blog

基本的に 対話モードじゃないとダメらしいです。

確かに 別のコマンドを alias されていたら危険ですね。

watch コマンドで一定間隔でコマンドを実行する

(使っている Linux は Ubuntu 14.04.3 LTS です)

watch コマンドは 指定したコマンドを一定間隔で実行して 出力結果の差分を表示してくれますが この「一定間隔でコマンドを実行する」という機能は それだけで便利です。

次のようにすると 3秒ごとに Webサーバにアクセスします。

$ watch -n 3 wget -q http://localhost/hogehoge -O - > /dev/null

繰り返すだけなら while などでもできますが 一定間隔で実行するなら watch コマンドを使うのが簡単で便利です。

Linux を ストップウォッチにする

(使っている Linux は Ubuntu 14.04.3 LTS です)

そんな状況があるかどうかわかりませんが Linux を使っていて ストップウオッチが必要になった場合の方法です。

$ time read

これだけです。

コマンドの実行で開始、 [Enter] を押すと時間が出ます。

$ time read

real    0m4.696s
user    0m0.000s
sys     0m0.000s

bash で 終了時に 一時ファイルを自動的に削除する

(使っている Linux は Ubuntu 14.04.3 LTS です)

シェルスクリプトで作成した 一時ファイルを 終了後に自動的に消します。

ポイントは 2点あります。

@子プロセスで作成した一時ファイルのパスを 親プロセスで取得するのは シェルスクリプトでは大変なので、 一時ファイルは最初に起動したシェルスクリプトが 作成するディレクトリの配下に格納します。

A最初に起動したシェルスクリプトの終了時に builtinコマンドの trap で 一時ファイルを削除する処理(function)を自動的に実行します。

まず、一時ファイル用のディレクトリを作成します。

declare -r SCRIPT_PATH=${BASH_SOURCE:-$0}
declare -r SCRIPT_NAME=$(basename "${SCRIPT_PATH}")
declare -r SCRIPT_TMP_DIR=$(mktemp -d -t "${SCRIPT_NAME}.XXXXXX")

何かあったときに調査しやすいように シェルスクリプトの名前を付けたディレクトリを作成します。

上記の場合 環境変数 TMPDIR に従って 次のようなディレクトリ名になります。 (TMPDIR が /tmpの場合)

/tmp/hogehhoge.sh.mBzqNx

この値を 環境変数 TMPDIR にセットします。

export TMPDIR=${SCRIPT_TMP_DIR}

これで 子プロセスや functionなどで 一時ファイルを作る際も mktemp に 環境変数 TMPDIR を使用するオプションを付けておけば 同じディレクトリに 一時ファイルを作成することができます。

次に 自動的に 一時ファイルを削除する仕組みです。

一時ファイルを削除して終了する functionを定義します。

function on_exit_event() {
    local script_exit_code=${1}
    rm -Rf "${SCRIPT_TMP_DIR}"
    exit ${script_exit_code}
}

rmコマンドのオプションに "-f" を付けてファイルがないときもエラーが出ないようにし、 "-R" で、ファイルではなく、一時ディレクトリを作成した場合も削除できるようにします。

これを trap で起動するようにします。

trap 'on_exit_event ${?}' EXIT

これで 一時ファイルが 最初に起動したシェルスクリプトの終了時に 自動的に削除されます。

全体は次のようになります。

#!/usr/bin/env bash

declare -r SCRIPT_PATH=${BASH_SOURCE:-$0}
declare -r SCRIPT_NAME=$(basename "${SCRIPT_PATH}")
declare -r SCRIPT_DIR=$(cd $(dirname "${SCRIPT_PATH}"); pwd)
declare -r SCRIPT_FULL_PATH=${SCRIPT_DIR}/${SCRIPT_NAME}

declare -r SCRIPT_TMP_DIR=$(mktemp -d -t "${SCRIPT_NAME}.XXXXXX")
export TMPDIR=${SCRIPT_TMP_DIR}

function on_exis_sub_event() { :; }

function on_exit_event() {
    local script_exit_code=${1}
    on_exis_sub_event
    rm -Rf "${SCRIPT_TMP_DIR}"
    exit ${script_exit_code}
}

trap 'on_exit_event ${?}' EXIT

(主処理)

on_exis_sub_eventは 終了時の処理を追加したくなった場合に 上書きするための functionです。 とりあえず 空で定義しておいて 必要に応じて 後で再定義します。

bash の ":"(コロン) は、builtin command

(使っている Linux は Ubuntu 14.04.3 LTS です)

bash のシェルスクリプトを読んでいると ときどき ":" (コロン) が登場します。 この ":" は bash の builtin です。

実行すると、、、

$ :

何も返ってきませんが エラーも出ません。

ちなみに ";"(セミコロン) だと 次のようにエラーが出ます。

$ ;
-bash: syntax error near unexpected token `;'

type コマンドで見ると builtin となってます。

$ type :
: is a shell builtin

次のようにコメントとして使うことができます。

: comment comment
: comment comment
: comment comment

コマンドプロンプトの REM と同じですね。

何も返さないので 次のようにして 空ファイルを作成することもできます。

$ : > blank.log

プロセス置換と組み合わせて 次のようにして空ファイルを作成することもできます。

$ cat <(:)> blank.log

知っていれば読めますが 知らないと記号にしか見えません。

bash の プロセス置換

script コマンドを使った小技です。
(使っている Linux は Ubuntu 14.04.3 LTS です)

bash には 「プロセス置換」という機能があります。

Process Substitution

    Process  substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming
    open files.  It takes the form of <(list) or >(list).  The process list is run with its input or  output  con‐
    nected  to a FIFO or some file in /dev/fd.  The name of this file is passed as an argument to the current com‐
    mand as the result of the expansion.  If the >(list) form is used, writing to the file will provide input  for
    list.   If  the  <(list)  form  is used, the file passed as an argument should be read to obtain the output of
    list.

    When available, process substitution is performed simultaneously with parameter and variable  expansion,  com‐
    mand substitution, and arithmetic expansion.

処理中の一時ファイルの作成を減らせる機能で、 無くても他の方法で代用は効くのですが 良い感じで使うとコマンドが読みやすくなったりします。

書き方は、次の 2つ。

<(コマンド)
>(コマンド)

名前の似てる「コマンド置換」と書き方も似てますね。

コマンド置換とプロセス置換

$(コマンド)   #コマンド置換

「コマンド置換」は、コマンドの結果を コマンドやコマンドの引数として使用することができました。

$ echo $(echo date)

date
$ $(echo date)

Thu Dec  8 22:58:19 JST 2018

「プロセス置換」は、コマンドの結果を ファイルの入力のように扱ったり 出力をコマンドに渡したりできます。

<(コマンド)

まずは "<(コマンド)" の方ですが、コマンドで入力ファイルのパスを 指定するところに書くことができます。

よく使われる例としては diff コマンドです。
diff コマンドは、2つのファイルを比較する際に 標準出力が 1つしか指定できないため 2つのコマンドの実行結果を比較したい場合 比較するコマンドの片方は実体が必要になります。

$ ls -l /var/xxxx > xxxx.txt
$ ls -l /var/yyyy > yyyy.txt
$ diff xxxx.txt yyyy.txt
$ ls -l /var/xxxx > xxxx.txt
$ ls -l /var/yyyy | diff - xxxx.txt

「プロセス置換」を使うと 一時ファイルを作成せずに 2つのコマンドの実行結果を そのまま比較することができます。

$ diff <(ls -l /var/xxxx) <(ls -l /var/yyyy)

コマンドも見やすいですね。
一時ファイルを削除する手間もなくなります。

echo で出力すると 「プロセス置換」の実体が ファイルデスクリプタだとわかります。

$ echo <(echo 1)

/dev/fd/63

わかりにくくなりますが 次のようなこともできます。

$ $(cat <(echo date))

Thu Dec  8 22:14:36 JST 2018

これは まず 次のように処理されて "date" が返ってきます。

$ echo date > /dev/fd/63
$ cat /dev/fd/63

返ってきた date を実行して 日時が出力されました。

$ date

Thu Dec  8 22:14:36 JST 2018

また 次のような do ... done のループで パイプでつなぐと 中の処理が別プロセスになり 環境変数を 上書きしてくれないような場合、、、

$ filename=none

$ ls | grep -v "test." | while read filepath
> do
>     filename=$(basename $filepath)
> done

$ echo $filename

none   #←別プロセスのため上書きされない

「プロセス置換」を使うと ファイルを指定するのと同じように 同じプロセスで処理することができます。

$ filename=none

$ while read filepath
> do
>     filename=$(basename $filepath)
> done < <(ls | grep -v "test.")

$ echo $filename

xxxxx.txt   #←同じプロセスのため上書きされた

>(コマンド)

次に ">(コマンド)" の方ですが、こちらは だいたいパイプでできてしまうため あまり良い例が思い浮かびませんでした。

よく使うのは次のように 標準エラー出力で 不要な行をオミットするケースすです。

$ command 2> >(grep -v ^Notice: >&2)

これは、標準エラー出力を一旦 プロセス置換で grep コマンドに渡して "Notice:" から 始まる行を省いて、再度標準エラー出力に渡しています。

他には tee コマンドと組み合わせて 特定の出力だけ振り分ける なんてことができます。

$ cat test.txt | \
>    tee >(grep ^case1 > case1.txt) \
>    tee >(grep ^case2 > case2.txt) \
>    tee >(grep ^case3 > case3.txt) \
>    > /dev/null

この例では、出力された行の先頭の文字によって 別のファイルに保存しています。

script コマンドで、別の端末の操作をモニタリングする

script コマンドを使った小技です。
(使っている Linux は Ubuntu 14.04.3 LTS です)

script コマンドは 作業のログをとってくれる便利なコマンドですが そのコマンドを利用すると 別の端末の操作をモニタリングすることができます。

端末 A

操作する側の 端末 A では、
script コマンドを "-f" オプション付きで実行します。

$ script -f /tmp/test.log
-f, --flush    run flush after each write

"-f" は 即時にログに出力するためのオプションで 接続が切れた場合にも安心のオプションです。

端末 B

モニタリングする側の 端末 B では、
tail コマンドを "-f" オプション付きで 実行し、 端末 A の script コマンドのログを参照します。

$ tail -f /tmp/test.log

この状態で 端末 A で操作をすると 端末 B のコンソールにも同じ内容が出力されます。

ダブルチェックや 指導する場合などに便利だと思います。

※ 間違えて "script -f" を実行した 端末 A で script コマンドの ログファイルを "tail -f" で開いてしまうと 無限ループに入ってしまうので注意してください。

差分ファイルだけを tar コマンドで固める

メモです。
(使っている Linux は Ubuntu 14.04.3 LTS です)

find コマンドで取得した更新日が新しいファイルだけを tar コマンドで 固める方法です。

更新日は、比較対象のファイルより新しいものを取得します。 (この方法については、前に書いているので、そちらを参照してください)

まずは環境変数の設定。
比較対象のファイル(BASE_FILE_PATH)と 固めるTARファイル(TAR_FILE_PATH)のパスを設定します。

$ BASE_FILE_PATH=./last_update.txt
$ TAR_FILE_PATH=/tmp/xxxxxx

既存のTARファイルが存在するとよろしくないため 念のためですが、0バイトで上書きしておきます。

$ cat /dev/null > ${TAR_FILE_PATH}.tar

find コマンドでファイルを取得しつつ tar コマンドで固めます。 このとき、tar コマンドでは 追記 "r" を指定するのと 圧縮の "z" は指定しないようにします。

$ find ./ -type f -newer ${BASE_FILE_PATH} -exec tar rf ${TAR_FILE_PATH}.tar {} \;

TAR ボールを作った後で圧縮します。

$ gzip -f ${TAR_FILE_PATH}.tar

最後に比較対象のファイルを touch コマンドで更新しておけば 次もこの後からの差分を取得することができます。

$ touch ${BASE_FILE_PATH}

find コマンドで日時を指定して検出する

メモです。
(使っている Linux は Ubuntu 14.04.3 LTS です)

find コマンドで -mtime オプションを使うと ファイルの更新日を条件にして「〜日前」のような検出ができます。

$ find ./ -mtime -3

具体的に日時を指定して検出するには 次のように -newermt オプションを使用します。 (-newermt の m は Modify の m で、a にすると Access、c だと Change になります)

$ find ./ -newermt "2018/10/30 12:00"

(日付の書式は色々指定できます)

これで指定した時刻よりもタイムスタンプが大きい(指定した時刻は含まれない)ファイルを検出することができます。

-newerXY reference

    Compares the timestamp of the current file with reference.  The reference argument is normally the name
    of a file (and one of its timestamps is used for the comparison) but it may also be a string describing
    an absolute time.  X and Y are placeholders for other letters, and  these  letters  select  which  time
    belonging to how reference is used for the comparison.

    a   The access time of the file reference
    B   The birth time of the file reference
    c   The inode status change time of reference
    m   The modification time of the file reference
    t   reference is interpreted directly as a time

    Some  combinations  are  invalid;  for example, it is invalid for X to be t.  Some combinations are not
    implemented on all systems; for example B is not supported on all systems.  If  an  invalid  or  unsup‐
    ported  combination  of XY is specified, a fatal error results.  Time specifications are interpreted as
    for the argument to the -d option of GNU date.  If you try to use the birth time of a  reference  file,
    and  the  birth  time cannot be determined, a fatal error message results.  If you specify a test which
    refers to the birth time of files being examined, this test will fail for any  files  where  the  birth
    time is unknown.

find コマンドで特定のファイル以降の更新を検出する

メモです。
(使っている Linux は Ubuntu 14.04.3 LTS です)

find コマンドで -mtime オプションを使うと ファイルの更新日を条件にして「〜日前」のような検出ができます。

$ find ./ -mtime -3

何かの作業で更新したファイル以降に作成・更新されたファイルを検出したい場合 -newer というオプションが使えます。

$ find ./ -newer hogehoge.txt
-newer file

    File  was  modified  more  recently  than file.  If file is a symbolic link and the -H option or the -L
    option is in effect, the modification time of the file it points to is always used.

このオプションは、引数のファイルのタイムスタンプよりタイムスタンプが新しいファイルを検出してくれます。

引数のファイルのタイムスタンプ“より大きい”ため、そのファイル自体は含まれません。 引数のファイルが対象の中にあっても、検出されないようになってるわけです。 よく考えられてますね。

tar コマンドでパスを変更して展開したい

メモです。
(使っている Linux は Ubuntu 14.04.3 LTS です)

tar コマンドで展開するときに 格納されているパスがそのまま使えずに パスの一部を変更して展開したいときがあります。

[格納されているパス]
home/hogehoge/test1/xxxxxx

[展開したいパス]
/home/hogehoge/test2/xxxxxx

次のようにオプションと変更内容を指定することで、 パスを変更しながら展開することができます。

$tar xzvf xxxxx.tar.gz --transform='s/test1/test2/g'
File name transformations:

    --transform=EXPRESSION, --xform=EXPRESSION
                           use sed replace EXPRESSION to transform file
                           names

ディレクトリの「 / 」も変更したい場合は 次のように区切り文字を「 | 」などに変更しておくと良いと思います。

$tar xzvf xxxxx.tar.gz --transform='s|hogehoge/test1|hugahuga/test2|g'

tar コマンドで一部だけを展開したい

メモです。
(使っている Linux は Ubuntu 14.04.3 LTS です)

tar コマンドで展開するときに 特定のファイルや、特定のディレクトリ以下のファイルだけを展開したいときがあります。

次のようにパスを指定することで、そのファイルだけを展開することができます。

$ tar xvzf xxxxx.tar.gz home/hogehoge/test.html

同様に、ディレクトリのパスを指定すると、そのディレクトリ以下を展開できます。

$ tar xvzf xxxxx.tar.gz home/hogehoge

パスの指定にはワイルドカードを使うこともできます。

$ tar xvzf xxxxx.tar.gz --wildcards */hogehoge.png
File name matching options (affect both exclude and include patterns):

    --wildcards            use wildcards (default for exclusion)
    --no-wildcards         verbatim string matching

余談ですが、TABキーの入力補完で .tar.gz ファイルの中のパスまで 指定できてビックリしました。

Bash のエラー発生時に終了するオプション

通常、Bash の処理は エラーが発生しても次のステップへと 進んで行きます。

[test1.sh]

true
echo 1=$?

false
echo 2=$?

true
echo 3=$?

false
echo 4=$?
$ bash test1.sh

1=0
2=1
3=0
4=1

エラーが発生したときに止めるには オプション "-e" を付けます。

$ bash -e test1.sh

1=0

また、パイプしている場合 最後のコマンドの結果が返ります。

[test2.sh]

true | true
echo 1=$?

true | false | true
echo 2=$?

false | true
echo 3=$?

true | false
echo 4=$?
$ bash test2.sh

1=0
2=0
3=0
4=1

これにオプション "-e" を付けて実行しても

$ bash -e test1.sh

1=0
2=0
3=0

最後のコマンドで発生しているエラーしか 反応してくれません。

このようなときは さらに オプション "-o pipefail" を指定します。

$ bash -e -o pipefail test1.sh

1=0

これでパイプ中のエラーが返るようになります。

Bash の未定義の変数をチェックするオプション

Bash には プログラム言語のように 未定義の変数をチェックできる オプション "-u" があります。

このオプションを指定していると 未定義の変数が出てきたところで エラーにしてくれます。

試してみます。

[test.sb]

A=a
C=c
echo A=$A
echo B=$B
echo C=$C

まずは普通に実行。

$ bash test.sh

A=a
B=
C=c

$ echo $?

0

変数 B だけ定義されていないので値が空です。

次にオプションを付けて実行。

$ bash -u test.sh

A=a
test.sh: line 4: B: unbound variable

$ echo $?

1

途中でエラーになりました。 安全ですね。

他のオプション同様 set コマンドなどでも指定できます。

Bash の実行内容のトレースのオプション

Bash には 処理の実行内容をトレースできる デバッグに便利な オプション "-x" と "-v" があります。

文法チェックの "-n" と同じ様に 色々な方法で指定できます。

$ bash -xv test.sh
#!/bin/bash -xv
set -xv

ちなみに、オプション "-x" は実行された内容を出力し オプション "-v" は実行するなコマンドを出力します。

次のシェルスクリプトで試してみます。

[test.sh]

YESTERDAY=$(date --date "1 day ago")
echo $YESTERDAY

まず普通に実行。

$ bash test.sh

Sun Oct 1 21:55:13 JST 2017

まず オプション "-x" だけを指定。

$ bash -x test.sh

++ date --date '1 day ago'
+ YESTERDAY='Sun Oct  1 21:55:23 JST 2017'
+ echo Sun Oct 1 21:55:23 JST 2017
Sun Oct 1 21:55:23 JST 2017

追加で出力された行の先頭に "+" や "++" が付いてます。

"++" は "$()" で実行された部分ですね。

変数が値に転換されています。

次に オプション "-v" だけを指定。

$ bash -v test.sh

YESTERDAY=$(date --date "1 day ago")
date --date "1 day ago"
echo $YESTERDAY
Sun Oct 1 21:55:28 JST 2017

こちらは "+" などは付かず、変数もそのままですね。

"$()" の実行が切りだされています。

さらに オプション "-x" "-v" を両方指定。

$ bash -xv test.sh

YESTERDAY=$(date --date "1 day ago")
date --date "1 day ago"
++ date --date '1 day ago'
+ YESTERDAY='Sun Oct  1 21:55:44 JST 2017'
echo $YESTERDAY
+ echo Sun Oct 1 21:55:44 JST 2017
Sun Oct 1 21:55:44 JST 2017

実行するコマンド、実行内容が全て出力されました。

また オプション "-x" "-v" の指定を取り消すには オプション "+x" "+v" を指定します。

$ set -x

$ YESTERDAY=$(date --date "1 day ago")

++ date --date '1 day ago'
+ YESTERDAY='Sun Oct  1 22:06:42 JST 2017'

$ echo $YESTERDAY

+ echo Sun Oct 1 22:06:42 JST 2017
Sun Oct 1 22:06:42 JST 2017

$ set +x

+ set +x

$ YESTERDAY=$(date --date "1 day ago")
$ echo $YESTERDAY

Sun Oct 1 22:06:57 JST 2017

$ set -v
$ YESTERDAY=$(date --date "1 day ago")

YESTERDAY=$(date --date "1 day ago")
date --date "1 day ago"

$ echo $YESTERDAY

echo $YESTERDAY
Sun Oct 1 22:07:09 JST 2017

$ set +v

set +v

$ YESTERDAY=$(date --date "1 day ago")
$ echo $YESTERDAY

Sun Oct 1 22:07:20 JST 2017

文法チェックのオプション "-n" と "-v" を組み合わせると 実行せずに、実行内容だけを確認することができます。

$ bash -nv test.sh

YESTERDAY=$(date --date "1 day ago")
echo $YESTERDAY

Bash の 文法チェックのオプション

Bash には 処理を実行せずに 文法のチェックだけをしてくれるオプション "-n" があります。

[test.sh]

if [ -z test.txt ]; then
    echo "OK"
endif
$ bash -n test.sh

test.sh: line 4: syntax error: unexpected end of file

このオプションを付けることで コマンドを実行せずに 文法だけをチェックしてくれます。
(逆にコマンドの実行などはチェックできません)

この手の Bash のオプションの指定方法は いくつかあります。

@bash コマンドの引き数に指定する。

$ bash -n test.sh

Aスクリプトの先頭に記載する。

#!/bin/bash -n

Bset コマンドで指定する。

set -n

スクリプトの起動だけを確認したいときなど 色々と応用できます。

Bash でループを別プロセスにしない

Bash で以下のようなループ処理を書くと パイプ以降が別プロセスになってしまうため その中で設定した変数を、ループ処理の外で参照することができません。

cat test1.txt | while read val1 val2
do
    val3=$val1
done

echo $val3 # ←最後のval1は入っていない

単純にテキストから読み込む場合 次のように書くと 別プロセスを作らずに済むため ループの中で設定した変数を ループの外で参照することができます。

while read val1 val2
do
    val3=$val1
done < test1.txt

echo $val3 # ←最後のval1が入っている

コマンドの実行結果をループに 渡している場合も・・・

cat test1.txt | grep -v "^a" | while read val1 val2
do
    val3=$val1
done

echo $val3 # ←最後のval1は入っていない

次ような書き方をすることができます。

while read val1 val2
do
    val3=$val1
done < <(cat test1.txt | grep -v "^a")

echo $val3 # ←最後のval1が入っている

( "<" の後の空白に注意してください)

プログラム的な処理が必要なときに 助かります。

Linux でプロセスの環境変数を確認する

以前、PostgreSQL のパスワードを 環境変数で設定する記事を書きましたが PostgreSQL 的には非推奨のようです。
(一部のオペレーティングシステムではroot以外のユーザで環境変数が見える場合がるためとのこと)

[参考]
環境変数 - PostgreSQL 9.3.2文書

一部のオペレーティングシステムがどれを指すのかわかりませんが、 プロセスが使ってる環境変数の見方が気になったので ちょっと確認してみました。

まず環境変数を設定します。

$ export PGPASSWORD=testpassword

PostgreSQL のデータベースに接続するプロセスを作成します。

$ psql -U testuser -d testdb

psql (9.3.10, server 9.3.12)
Type "help" for help.

testdb=#

[Ctrl]+[z] プロセスを眠らせます。

[1]+  Stopped   psql -U testuser -d testdb

起動中のプロセスを確認します。

$ ps

  PID TTY          TIME CMD
28796 pts/0    00:00:00 bash
29754 pts/0    00:00:00 psql
29834 pts/0    00:00:00 ps

参考サイトによると、環境変数は「/proc/%{PID}/environ」に 格納されているそうです。

[参考]
Linux/Unixプロセス起動時の環境変数をダンプする | ギークを目指して

確認してみます。

$ cat /proc/29754/environ | sed -e 's/\x0/\n/g' | grep PGPASSWORD

PGPASSWORD=testpassword

たしかに確認できました。

使用している環境では root ユーザでなければ 自分以外の環境変数は見れませんでしたが、 自分の環境変数を(rootの権限が使えれば、他のプロセスの環境変数も)確認したいこともあると思うので 覚えておくと良いのかもしれません。

ワイルドカードで特定のファイルだけを除外する

通常、ワイルドカードを使うと 以下のようになるところを・・・。

$ ls -1 *.txt

1-1.txt
1-2.txt
2-1.txt
2-2.txt
3-1.txt

"!" を使うことで、逆に除外することができます。

$ ls -1 !(*-2.txt)

1-1.txt
2-1.txt
3-1.txt

この例では「*-2.txt」にマッチしないファイルが 表示されました。

tail コマンドでファイルが再作成されても追いかける

tail コマンドの "-f" オプションは "--follow=descriptor" ということで 対象のファイルが再作成されると追いかけられなくなります。

$ tail -f test.txt

そういうときは、"--follow=name" か "-F" を使います。

$ tail -F test.txt
  -f, --follow[={name|descriptor}]
                    output appended data as the file grows;
                    -f, --follow, and --follow=descriptor are
                    equivalent
  -F                same as --follow=name --retry
$ tail -F test.txt

Fri Sep  9 12:14:01 JST 2017
Fri Sep  9 12:14:02 JST 2017
tail: 'test.txt' has become inaccessible: No such file or directory
tail: 'test.txt' has appeared;  following end of new file
Fri Sep  9 12:14:12 JST 2017
Fri Sep  9 12:14:13 JST 2017

ちなみに ファイルが再作成されると上のように表示されます。

Linux のパスを展開する glob

ファイルやディレクトリを操作するときに "*" や "?" などのワイルドカードを使いますが これが何によって定義されているのかを 考えたことがありませんでした。

これ、 Linux では glob という名前だそうです。

[参考]
Dev Basics/Keyword:glob(グロブ) - @IT

記事を読んで驚いたのが Linux では文字クラス([0-9a-z]みたいなやつ)も使えるとのこと。 Windows のコマンドプロンプトでは使えないため ワイルドカードだけだと思い込んでました。

$ ls -1 /var/log/vsftpd.log*

/var/log/vsftpd.log
/var/log/vsftpd.log.1
/var/log/vsftpd.log.2
/var/log/vsftpd.log.3
/var/log/vsftpd.log.4

$ ls -1 /var/log/vsftpd.log.?

/var/log/vsftpd.log.1
/var/log/vsftpd.log.2
/var/log/vsftpd.log.3
/var/log/vsftpd.log.4

$ ls -1 /var/log/vsftpd.log.[2-3]

/var/log/vsftpd.log.2
/var/log/vsftpd.log.3

$ ls -1 /var/log/vsftpd.log.[^3-5]

/var/log/vsftpd.log.1
/var/log/vsftpd.log.2

うーん、これは便利。

こういうのは当たり前過ぎて、 知ってる人は「他の人も知ってる」と思って わざわざ伝えないものなのかもしれないですね。

ちなみに、ファイルをコピーするときによく使う {} の展開も glob の仕事だそうです。

$ ls -1 /var/log/syslog*

/var/log/syslog

$ cp -p /var/log/syslog{,.keep}

$ ls -1 /var/log/syslog*

/var/log/syslog
/var/log/syslog.keep

[参考]
用語集:ファイルグロブ: UNIX/Linuxの部屋

find の日付検索の基準日時

find の "-mtime" などの日付検索の オプションは便利ですが コマンドの実行した時点が 基準日時(そこから24時間以内など)になるため ややこしかったりすることがあります。

ケースにもよりますが、 日付検索の 便利なオプションに
"-daystart" があります。

使い方は簡単。付けるだけ。

$ find . -daystart -mtime -3

"-daystart" は "-mtime" などより先に書きます。

このオプションで、日付検索の基準が00時00分になるため 現在時刻ではなく、日付単位で考えることができるようになります。

date コマンドで、日付の書式変換

小ネタ。

$ date -d "2017/3/4 1:15" +"%Y/%m/%d %H:%M"

2017/03/04 01:15

date コマンドに 日付文字列を与えて 出力の書式を指定すれば、数字の0詰めも簡単。

Linux 権限の後ろのドットについて

Redhat7.2を触っていて、おや?と思いました。

$ ls -l

-rw-r--r--. 1 root root 108  9月 25  2015 xxxxxxxxxxx

ls すると、権限の後ろにドットが付いていました。

調べてみると、そのドットは「SELinuxのセキュリティコンテキストが設定されているよ」とのこと。

[参考]
RHEL6で、ls -l 打った時に権限の後ろに".(ドット)"が付く、付かないは何を意味してる? - Qiita

ls に "-Z" 付けてみると、セキュリティコンテキストが表示されるようです。

-Z, --context Display security context so it fits on most
              displays.  Displays only mode, user, group,
              security context and file name.
$ ls -Z

-rw-r--r--. root root system_x:object_x:system_xxxx:s0 xxxxxxxxxxx

色々と知らないことが増えるなぁ、という話。

test コマンドの -v オプション

Bash シェルを実行するときに 環境変数の設定の有無を確認しているのですが 基本文字列なので、パッと見が分かり易い 次のような書き方をよくしていました。

if [ "${VAR_XXXXX}" = "" ]; then
   echo "環境変数が未設定"

値が必須だったので、良かったのですが 今回たまたま配列を使うことになり、 同じ書き方をすると未設定にマッチしてしまいました。

調べてみると最近は test コマンドに -v オプションがあり 変数の定義の有無をチェックできるとか。

if [ ! -v VAR_XXXXX ]; then
   echo "環境変数が未設定"

これでイケました。
まだ環境は選ぶようですが、見た目もわかりやすくて良いですね。

Ubuntu14.04 で、sendmail のオプションが使えなくなってた話

前回に続き、Ubuntu14.04 メモです。

Ubuntu10.04 では、以下のように mail コマンドで sendmail のオプションが使用できていました。

MAIL(1)                                      BSD General Commands Manual                                      MAIL(1)

NAME
     mail, mailx, Mail ― send and receive mail

SYNOPSIS
     mail [-dEIinv] [-a header] [-b bcc-addr] [-c cc-addr] [-s subject] to-addr ... [-- sendmail-options ...]
     mail [-dEIiNnv] -f [file]
     mail [-dEIiNnv] [-u user]

ハイフン 2 つ付けて以下のようにしていました。

$ mail -s "subject" to@xxx -- -f from@xxx

Ubuntu14.04 では、sendmail のオプションが 指定できなくなっていました。 (指定すると、送信先のアドレスと判定されてしまいます)

MAILX(1)                                            User Commands                                            MAILX(1)

NAME
       mailx - send and receive Internet mail

SYNOPSIS
       mailx [-BDdEFintv~] [-s subject] [-a attachment ] [-c cc-addr] [-b bcc-addr] [-r from-addr] [-h hops]
              [-A account] [-S variable[=value]] to-addr . . .
       mailx [-BDdeEHiInNRv~] [-T name] [-A account] [-S variable[=value]] -f [name]
       mailx [-BDdeEinNRv~] [-A account] [-S variable[=value]] [-u user]

前回の話のように bsd-mailx から heirloom-mailx に 替わったからかと思い mail コマンドを bsd-mailx に戻してみました。

$ sudo update-alternatives --config mailx
There are 3 choices for the alternative mailx (providing /usr/bin/mailx).

  Selection    Path                     Priority   Status
------------------------------------------------------------
* 0            /usr/bin/heirloom-mailx   60        auto mode
  1            /usr/bin/bsd-mailx        50        manual mode
  2            /usr/bin/heirloom-mailx   60        manual mode
  3            /usr/bin/mail.mailutils   30        manual mode

Press enter to keep the current choice[*], or type selection number: 1
update-alternatives: using /usr/bin/bsd-mailx to provide /usr/bin/mailx (mailx) in manual mode

しかし やはり、sendmail のオプションが なくなっていました。

MAIL(1)                                      BSD General Commands Manual                                      MAIL(1)

NAME
     mail, mailx, Mail ― send and receive mail

SYNOPSIS
     mail [-dEIinv] [-a header] [-b bcc-addr] [-c cc-addr] [-s subject] to-addr ...
     mail [-dEIiNnv] -f [file]
     mail [-dEIiNnv] [-u user]

ちなみに Ubuntu14.04 の heirloom-mailx では、送信元は以下のように指定します。

$ mail -s "subject" -r from@xxx to@xxx

Ubuntu14.04 で、標準メールが heirloom-mailx になってた話

Ubuntu14.04 での話です。

mail コマンドのシンボリックリンクを追っていきます。

$ readlink -f `which mail`
/usr/bin/heirloom-mailx

heirloom-mailx です。

Ubuntu10.04 では、bsd-mailx でした。

$ readlink -f `which mail`
/usr/bin/bsd-mailx

複数のメールパッケージを入れている場合は update-alternatives コマンドで 切り替えることができます。

$ sudo update-alternatives --config mailx
There are 3 choices for the alternative mailx (providing /usr/bin/mailx).

  Selection    Path                     Priority   Status
------------------------------------------------------------
* 0            /usr/bin/heirloom-mailx   60        auto mode
  1            /usr/bin/bsd-mailx        50        manual mode
  2            /usr/bin/heirloom-mailx   60        manual mode
  3            /usr/bin/mail.mailutils   30        manual mode

Press enter to keep the current choice[*], or type selection number:

LC_ALL と LANG を設定した時の違い

ちょっと気になったのでメモ。 環境は Ubuntu14.04 です。

設定する前。

$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

LC_ALL に C を設定した場合。

$ env LC_ALL=C locale
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=C

LANG に C を設定した場合。

$ env LANG=C locale
LANG=C
LANGUAGE=en_US:en
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=

LC_ALL に設定した場合は LANG は変わりませんでした。

コマンドの結果の2行目移行をソートする

1行目にヘッダーが出力されるコマンドの結果の 2行目以降のみをソートしようと思って力技。

$ df -T | sed '1d' | sort

sed で 1行目を削除しています。

で、ヘッダー行を追加。

$ (df -T | head -n 1) && (df -T | sed '1d' | sort)

Filesystem     Type     1K-blocks    Used Available Use% Mounted on
/dev/sda1      ext3      19091584 2018084  16097016  12% /
none           tmpfs       102400       0    102400   0% /run/user
none           tmpfs     16432604       0  16432604   0% /run/shm
none           tmpfs            4       0         4   0% /sys/fs/cgroup
none           tmpfs         5120       0      5120   0% /run/lock
tmpfs          tmpfs      3286524     844   3285680   1% /run
udev           devtmpfs  16421864       4  16421860   1% /dev

netstat でルーティング情報を表示する

知らないオプションだったので、メモ。

$ netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
localnet        *               255.255.255.0   U         0 0          0 eth0
default         192.168.1.1     0.0.0.0         UG        0 0          0 eth0

2つのコマンドの出力を1つにまとめる

たいしたことではないのですが。

コマンドの結果にヘッダーを付けたかったんですが cat でできたっけ?どうしようかと考えて結果力技。

$ (echo "MByte   Directory") && (du -sm /home /opt /tmp /var)

MByte   Directory
1       /home
1       /opt
87      /tmp
2       /var

コマンドをカッコで囲んで && でつなげたらできました。

Linux でグループにユーザを追加する

Linux でユーザを追加する方法はいくつかりますが usermod を使う場合は注意が必要です。

$ usermod -aG "グループ名" "ユーザ名"

このとき、"-G" だけだと現在の設定されているグループが消えてしまうので "-aG" のように "a" を付け忘れないようにします。

gpasswd というコマンドを使う方法もあります。

$ gpasswd -a "ユーザ名" "グループ名"

このコマンドは、グループを操作するコマンドなので グループ名が最後に来ることに注意します。 ユーザ名は、"-a" オプションの引き数になります。

読み取り専用で開く view コマンド

vi で開きたいけど 上書きしてしまうと怖いな というときに 読み取り専用で vi を開く view というコマンドがあります。

開くときは vi と同じです。

$ view test.txt

これなら ちょっと安全に使えますね。

ちなみに 読み取り専用で開きますが :w! のように感嘆符付きだと上書きできます。

touch コマンドで相対的な日付を指定する

サーバのバッチ処理なんかを作っていると、 「昨日のタイムスタンプのファイル」が 欲しくなるときがあります。

touch コマンドで日付を指定したり、 他のファイルを元に タイムスタンプを設定することはできますが 文字を使った相対的な指定だと 簡単に設定することができます。

$ touch -d "1 day ago" test.txt

ago じゃなくて負でも OK です。

$ touch -d "-1 hour" test.txt

複数指定できます。

$ touch -d "-2 day -1 hour" test.txt

次の例だと合計して 4 日前になります。

$ touch -d "-2 day -2 day" test.txt

この文字の相対指定は、 date コマンドでも使用できるので 事前にどうなるか確認することができます。

$ date -d "-2 day -2 hour"

seq という便利っぽいコマンド

以下の記事を読んで、seq なんてコマンドがあるんだ・・・と思ったのでメモ。

[参考]
seqコマンドがとても便利だった件 - ぶていのログでぶログ

試してみましょう。

$ seq 3
1
2
3

良いですね。

開始終了を指定できるようです。

$ seq 2 5
2
3
4
5

減算もできるみたいです。

$ seq 10 -2 5
10
8
6

他にも桁をそろえたり、区切り文字を変えたり(デフォルトは改行)、書式を指定したりできるようです。

$ seq -s, 2 8
2,3,4,5,6,7,8

$ seq -w 9 12
09
10
11
12

$ seq -f "%03g" 2 4
002
003
004

こんな使い方も。(2日ごとの日付を生成)

$ for i in `seq 0 2 30`; do date +"%Y-%m-%d (%a)" -d "$i day"; done;
2015-08-26 (Wed)
2015-08-28 (Fri)
2015-08-30 (Sun)
2015-09-01 (Tue)
2015-09-03 (Thu)
2015-09-05 (Sat)
2015-09-07 (Mon)
2015-09-09 (Wed)
2015-09-11 (Fri)
2015-09-13 (Sun)
2015-09-15 (Tue)
2015-09-17 (Thu)
2015-09-19 (Sat)
2015-09-21 (Mon)
2015-09-23 (Wed)
2015-09-25 (Fri)

簡単に生成する数字を指定できるので 確かに便利ですね。

bash で前のコマンドの引数だけを使用する

コマンドを実行するときに コマンドの前に echo を付けて試してみることがあります。

$ export testfile=dummy.txt
$ echo rm $testfile
rm dummy.txt

この後、echo を消して実行するわけですが !* を使うことで 同じことを簡単に実行できます。

$ export testfile=dummy.txt
$ echo rm $testfile
rm dummy.txt

$ !*
rm $testfile
rm: cannot remove `dummy.txt': No such file or directory

!* には、前に実行したコマンドの「コマンドより後ろのワード」が格納されるため echo の後ろから実行されるわけです。

!^ だと「コマンドより後ろのワード」の先頭、 !$ が「コマンドより後ろのワード」の最後になります。

$ echo 1 2 3 4 5
1 2 3 4 5

$ echo !^
echo 1
1

$ echo 1 2 3 4 5
1 2 3 4 5

$ echo !$
echo 5
5

ちなみに前に実行したコマンド全体は !! に格納されるため !! だけだと、前のコマンドを再実行になります。

$ echo 1 2 3 4 5
1 2 3 4 5

$ !!
echo 1 2 3 4 5
1 2 3 4 5

ちなみに、この「コマンドより後ろのワード」はパイプやリダイレクトも含みます。

$ echo 1 2 3 4 5 > /tmp/test.txt

$ echo !$
echo /tmp/test.txt
/tmp/test.txt

使うときは、ちょっとだけ注意です。

リダイレクトだけで 0バイト上書き

bash では、次のようにリダイレクトだけで 0バイトのファイルを作ることができるようです。

これまではコロンリダイレクトとか /dev/null を使ってました。

$ : > zero.txt
$ cat /dev/null > zero.txt

リダイレクトだけでいけます。

$ > zero.txt

$ ls -l zero.txt
-rw-r--r-- 1 xxxxxx xxxxxx 0 2015-08-19 21:24 zero.txt

シンプルですが、書き間違いと思われそうですね。

手作業中に使用するのには便利で良さそうです。

SQLite のデータベースファイルのバージョンを確認する

SQLite で作成したデータベースのファイルが SQLite のどのバージョンで作成したかですが file コマンドで確認することができました。 (Ubuntu10.04環境)

$ file database.sqlite2
database.sqlite2: SQLite 2.x database

$ file database.sqlite3
database.sqlite3: SQLite 3.x database

Linux でテキストからランダムに 1行取得する

おひさしぶりです。

cowsay コマンドでランダムに絵柄を変えたいと思っていたんですが、 そのためにはランダムで絵柄を指定する必要があります。

絵柄の一覧は次のようなコマンドで取得できます。

$ cowsay -l | grep -v "^Cow"

ここから、ランダムに 1行取得します。 最初は Bash の配列を使って 次のようにやってました。

$ LIST=(`cowsay -l | grep -v "^Cow"`)
$ FMT=${LIST[$(($RANDOM % ${#LIST[@]}))]}

1行でできないかと調べていたら sort コマンドには -R オプションが あったりすることを知りました。

次のようになりました。

$ cowsay -l | grep -v "^Cow" | sed -e "s/ /\n/g" | sort -R | head -n 1

cowsay と fortune も入れて、これで完成です。

$ fortune | cowsay -n -f `cowsay -l | grep -v "^Cow" | sed -e "s/ /\n/g" | sort -R | head -n 1`

・・・と思っていたら、shuf なんてコマンドもあるようで 次のようになりました。

$ fortune | cowsay -n -f `cowsay -l | grep -v "^Cow" | sed -e "s/ /\n/g" | shuf -n 1`

色々とありますね。

テキスト処理は覚えておくと、開発中のちょっとしたことに使えて便利なので 遊びながら覚えていきたいものです。

watch コマンド

Linux には watch という便利なコマンドがあります。

ディレクトリにファイルが書きだされるのを待ってるときに よく ls コマンドを連打するのですが watch コマンドを使えば、それを代わりにやってくれます。

$ watch ls -l /tmp

これで、(デフォルトの)2秒間隔で結果を表示してくれます。

間隔を変更するときは "-n" オプションです。

$ watch -n 5 ls -l /tmp

これで 5秒間隔になります。

"-d" オプションで、変更箇所をハイライトすることもできます。

$ watch -d ls -l /tmp

ただし、次の更新タイミングでハイライトが消えてしまうので ハイライトを残したい場合は "-d" オプションに 更に "=cumulative"を付けます。

$ watch -d=cumulative ls -l /tmp

bash の cd - と 環境変数 OLDPWD

bash では cd - とすることで 1つ前(cd する前)のディレクトリに移動することができます。

/var$ cd -
/tmp
/tmp$

この 1つ前のディレクトリのパスは 環境変数 OLDPWD に持っています。

1度も cd を使っていない場合 環境変数 OLDPWD は 空なので cd - しても ディレクトリを移動できません。

~$ bash
~$ cd -
bash: cd: OLDPWD not set

逆に 環境変数 OLDPWD に 値を設定すれば cd - で そのディレクトリに移動できます。

~$ export OLDPWD=/tmp
~$ cd -
/tmp
/tmp$

find コマンドの -exec オプションの + と ; について

find の man を見ていたら -exec について 以下のように書いてありました。

-exec command ;
-exec command {} +

プラス? と思いつつも英語を読まずに即検索。 書いてあるページがありました。

[参考]
Linux - find の -exec optionの末尾につく \; と + の違い。 - Qiita (2013/10/01)

$ find . -exec echo {} \;
./test1.txt
./test2.txt
./test3.txt
$ find . -exec echo {} +
./test1.txt ./test2.txt ./test3.txt

なるほど。

join コマンドでできることイロイロ

Linux には join という SQL使いにも馴染み易そうなコマンドがあります。

このコマンドは 2つのファイル(片方は標準入力可)の 指定したフィールドで結合した結果を返してくれます。

[text1.txt]

11111 aa1
22222 bb1
33333 cc1
55555 ee1
[text2.txt]

11111 aa2
33333 cc2
44444 dd2
66666 ff2

フィールドを指定しないと 1つ目のフィールドで結合します。

$ join test1.txt test2.txt

11111 aa1 aa2
33333 cc1 cc2

INNER JOIN ですね。
test1.txt と test2.txt の両方に存在する "11111" と "33333" が出力されました。

外部結合もできます。

$ join -a1 test1.txt test2.txt

11111 aa1 aa2
22222 bb1
33333 cc1 cc2
55555 ee1

LEFT JOIN になりました。
test1.txt からは全行が出力されています。

RIGHT JOIN もできます。

$ join -a2 test1.txt test2.txt

11111 aa1 aa2
33333 cc1 cc2
44444 dd2
66666 ff2

test2.txt の全行になりました。

"-a1" "-a2" を両方指定することもできます。

$ join -a1 -a2 test1.txt test2.txt

11111 aa1 aa2
22222 bb1
33333 cc1 cc2
44444 dd2
55555 ee1
66666 ff2

これで、どちらかのファイルに フィールドが存在する行が 出力されました。

cut コマンドなどで切り出せば 何かに使えそうですね。

$ join -a1 -a2 test1.txt test2.txt | cut -f1

11111
22222
33333
44444
55555
66666

フィールドがマッチしない場合に 出力することもできます。

$ join -v1 test1.txt test2.txt

22222 bb1
55555 ee1

test1.txt にのみフィールドが存在する行が 出力されました。

"-v1" と "-v2" を どちらも指定すると どちらかのファイルにのみフィールドが存在する行が出力されます。

$ join -v1 -v2 test1.txt test2.txt

22222 bb1
44444 dd2
55555 ee1
66666 ff2

指定したフィールドでの比較なので diff とはちょっと違いますね。

2つのファイルの差分なので 何かのチェックに使えそうです。

$ join -v1 -v2 test1.txt test2.txt | cut -f1

22222
44444
55555
66666

行番号を付ける nl コマンド

Linux には行番号を付ける nl というコマンドがあります。

行番号付けるだけではないのですが 深く掘り下げた使い方はしません。

[test.txt]

aaaaaaaaa
bbbbbbbbb
ccccccccc
ddddddddd
$ nl test.txt
     1  aaaaaaaaa
     2  bbbbbbbbb
     3  ccccccccc
     4  ddddddddd

こんな感じです。

これくらいなら cat でもできます。

$ cat -n test.txt
     1  aaaaaaaaa
     2  bbbbbbbbb
     3  ccccccccc
     4  ddddddddd

nl はオプションなしだと空行は番号を付けません。

$ nl test.txt
     1  aaaaaaaaa
     2  bbbbbbbbb
        
     3  ddddddddd
     4  eeeeeeeee

空行もにも番号を付ける場合はオプションを指定します。

$ nl -b a test.txt
     1  aaaaaaaaa
     2  bbbbbbbbb
     3
     4  ddddddddd
     5  eeeeeeeee

nl なら簡単に初期値を変えることもできます。

$ nl -v 3 test.txt
     3  aaaaaaaaa
     4  bbbbbbbbb
     5  ccccccccc
     6  ddddddddd

1行ごとの増加値も指定することができます。

$ nl -i 3 test.txt
     1  aaaaaaaaa
     4  bbbbbbbbb
     7  ccccccccc
    10  ddddddddd

awk などでもできなくはないですが 簡単なので覚えておくと便利です。

ls コマンドのタイムスタンプの書式

ls コマンドで表示されるタイムスタンプ書式は 環境変数 LANG によって変わります。

Ubuntu10.04 の ls だと次のようになります。

LAMG=C

$ env LANG=C ls -lt

-rw-r--r-- 1 root root  847 May 27 15:41 snmpd
-rw-r--r-- 1 root root  258 Nov 28  2013 samba
-rw-r--r-- 1 root root  876 Mar 17  2011 exim4
-rw-r--r-- 1 root root   64 Mar 17  2011 ntpdate

タイムスタンプが古い場合は 時刻ではなく、年が表示されます。

LANG=ja_JP.UTF-8

$ env LANG=ja_JP.UTF-8 ls -lt

-rw-r--r-- 1 root root  847 May 27 15:41 snmpd
-rw-r--r-- 1 root root  258 Nov 28  2013 samba
-rw-r--r-- 1 root root  876 Mar 17  2011 exim4
-rw-r--r-- 1 root root   64 Mar 17  2011 ntpdate

LAMG=C と同じですね。

LANG=en_US.UTF-8

$ env LANG=en_US.UTF-8 ls -lt

-rw-r--r-- 1 root root  847 2014-05-27 15:41 snmpd
-rw-r--r-- 1 root root  258 2013-11-28 17:56 samba
-rw-r--r-- 1 root root  876 2011-03-17 11:20 exim4
-rw-r--r-- 1 root root   64 2011-03-17 11:12 ntpdate

YYYY-MM-DD hh:mm になります。

指定したいときは "--time-style" オプションを使います。

$ ls -lt --time-style=+"%Y/%m/%d %H:%M:%S"

-rw-r--r-- 1 root root  847 2014/05/27 15:41:05 snmpd
-rw-r--r-- 1 root root  258 2013/11/28 17:56:50 samba
-rw-r--r-- 1 root root  876 2011/03/17 11:20:45 exim4
-rw-r--r-- 1 root root   64 2011/03/17 11:12:01 ntpdate

これで好きな書式で表示できます。

find コマンドで複数のコマンドを実行する

メモ。

$ find /usr/local/lib/ -type d -exec dirname {} \; -exec basename {} \;

/usr/local
lib
/usr/local/lib
python2.6
/usr/local/lib/python2.6
site-packages
/usr/local/lib/python2.6
dist-packages

-exec オプションを複数回使う。

find コマンドでディレクトリの階層が深い方から取得する

メモ。

$ find /usr/local/lib/ -depth -type d

/usr/local/lib/python2.6/site-packages
/usr/local/lib/python2.6/dist-packages
/usr/local/lib/python2.6
/usr/local/lib/

-depth オプションを付ける。

Raspberry Pi に MRTG を入れてみる (3)

前回の続き SNMP の設定です。 とりあえず SNMP からデータを取得できるように設定していきます。

まず snmpd の設定ファイルを編集します。

$ sudo vi /etc/snmp/snmpd.conf

内容は次のような感じです(全文)。

###############################################################################
#
#  AGENT BEHAVIOUR
#

#  Listen for connections from the local system only
agentaddress  udp:161


###############################################################################
#
#  ACCESS CONTROL
#

#       sec.name          source        community
com2sec PI_LOCAL        localhost       RASPBERRY_PI
com2sec PI_NETWOEK      192.168.3.0/24  RASPBERRY_PI

#       group.name      sec.model       sec.name
group   LOCAL_GROUP     v1              PI_LOCAL
group   LOCAL_GROUP     v2c             PI_LOCAL
group   LOCAL_GROUP     usm             PI_LOCAL
group   NETWORK_GROUP   v1              PI_NETWOEK
group   NETWORK_GROUP   v2c             PI_NETWOEK
group   NETWORK_GROUP   usm             PI_NETWOEK

#       view.name       incl/excl       subtree mask
view    all             included        .1      80

#       group.name      context sec.model       sec.level       match   read    write   notif
access  LOCAL_GROUP     ""      any             noauth          exact   all     none    none
access  NETWORK_GROUP   ""      any             noauth          exact   all     none    none


###############################################################################
#
#  SYSTEM INFORMATION
#

syslocation     Raspberry Pi raspbian
syscontact      Studio ODIN <xxxxx@xxxxx.xx.xx>
sysservices     72

ネットワークは 192.168.3.x を使用しているので、その範囲からのアクセスを許可しています。 community 名の "RASPBERRY_PI" はパスワードの意味も含まれているので、 コピペする場合は、適宜変更してください。

syslocation と syscontact は、ただの情報ですので適当に。

設定ファイルの編集が終わったら サービスを再起動します。

$ sudo service snmpd restart

あとは試しに snmpd にアクセスしてみます。

$ sudo snmpwalk -v 2c -c RASPBERRY_PI localhost 1.3.6.1.4.1.2021

"RASPBERRY_PI" は、設定ファイル内の community 名、 "1.3.6.1.4.1.2021" が取得する情報の識別子です。

このコマンドを実行して、文字列がダーっと帰ってきたら成功です。

今回はここまで。

find コマンドでリンク先も探す

メモ。

$ find -follow 

-follow オプションを付ける。

Raspberry Pi に MRTG を入れてみる (2)

短いですが、前回の続き。

前回、 MRTG でグラフ化するデータを取得するために SNMP のパッケージも入れました。

インストールすだけで起動しているので見てみます。

$ netstat -lun

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address   Foreign Address   State
udp        0      0 127.0.0.1:161   0.0.0.0:*

このように UDP 127.0.0.1 で待機しています。

サーバ内部で使用する場合はこれでも良いのですが 別のサーバからも SNMP にアクセスすることを 考えて設定を変更します。

SNMP の設定ファイルを編集します。

$ sudo vi /etc/snmp/snmpd.conf

これを。

#  Listen for connections from the local system only
agentAddress  udp:127.0.0.1:161

こうします。

#  Listen for connections from the local system only
agentAddress  udp:161

あとはサービスを再起動。

$ sudo /etc/init.d/snmpd restart

これで別サーバからもアクセスできるようになりました。

$ netstat -lun

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address   Foreign Address   State
udp        0      0 0.0.0.0:161     0.0.0.0:*

今回はここまで。

Raspberry Pi に MRTG を入れてみる (1)

ちょっと MRTG の復習がてら Raspberry Pi に MRTG を入れてみたいと思います。

とりあえずパッケージを最新の状態に。

$ sudo apt-get update

ちなみに 2014-01-07-wheezy-raspbian が入っています。

$ sudo apt-get upgrade

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages have been kept back:
  wolfram-engine
The following packages will be upgraded:
  curl dpkg dpkg-dev file libcurl3 libcurl3-gnutls libdpkg-perl libgnutls26 libmagic1 libxfont1 libyaml-0-2
  lsb-base python3-pifacedigital-scratch-handler tzdata udisks wget
16 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
Need to get 8,296 kB of archives.
After this operation, 570 kB of additional disk space will be used.
Do you want to continue [Y/n]?

インストール前後のパッケージのリストも取得しておきます。

$ sudo dpkg -l > ~/package_list_before_mrtg.txt

MRTG と SNMP のデーモンとクライアントを入れます。

$ sudo apt-get install snmpd snmp mrtg

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  libio-socket-inet6-perl libperl5.14 libsensors4 libsnmp-base libsnmp-session-perl libsnmp15
  libsocket6-perl
Suggested packages:
  lm-sensors snmp-mibs-downloader mrtg-contrib
The following NEW packages will be installed:
  libio-socket-inet6-perl libperl5.14 libsensors4 libsnmp-base libsnmp-session-perl libsnmp15
  libsocket6-perl mrtg snmp snmpd
0 upgraded, 10 newly installed, 0 to remove and 1 not upgraded.
Need to get 5,863 kB of archives.
After this operation, 9,507 kB of additional disk space will be used.
Do you want to continue [Y/n]?

途中で "Make /etc/mrtg.cfg owned by and readable only by root?" とか聞かれるので "Yes" と答えておきます。

$ sudo dpkg -l > ~/package_list_after_mrtg.txt

インストール前後のパッケージの差分は次のような感じ。

$ cd
$ diff package_list_after_mrtg.txt package_list_before_mrtg.txt

292d291
< ii  libio-socket-inet6-perl 2.69-2           all   object interface for AF_INET6 domain sockets
357d355
< ii  libperl5.14             5.14.2-21+rpi2   armhf shared Perl library
403d400
< ii  libsensors4:armhf       1:3.3.2-2+deb7u1 armhf library to read temperature/voltage/fan sensors
413,416d409
< ii  libsnmp-base            5.4.3~dfsg-2.7   all   SNMP (Simple Network Management Protocol) MIBs and documentation
< ii  libsnmp-session-perl    1.13-1           all   Perl support for accessing SNMP-aware devices
< ii  libsnmp15               5.4.3~dfsg-2.7   armhf SNMP (Simple Network Management Protocol) library
< ii  libsocket6-perl         0.23-1           armhf Perl extensions for IPv6
549d541
< ii  mrtg                    2.17.4-2         armhf multi router traffic grapher
639,640d630
< ii  snmp                    5.4.3~dfsg-2.7   armhf SNMP (Simple Network Management Protocol) applications
< ii  snmpd                   5.4.3~dfsg-2.7   armhf SNMP (Simple Network Management Protocol) agents

なんか色々と入りました。

今回はここまで。

Raspberry Pi の rasbian を 2014-01-07 にする

久しぶりの Raspberry PI ネタです。

今回は raspbian を配布されている最新にしたいと思います。 というわけで、 2014-01-07-wheezy-raspbian を入れます。

[参考サイト]
Raspberry Pi メモ (26) まとめ - Jun's homepage

いつものように 上記サイトを参考に進めます。

ダウンロードして、解凍して、SDカードに書き込んで起動。

2013-12-20-wheezy-raspbian で Mathematica が入ってます。 適当にコマンドを実行。

$ mathematica -v
10.0

なんか出ました。

2013-09-25-wheezy-raspbian から Java が入ってます。

$ java -version
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) Client VM (build 24.0-b56, mixed mode)

とりあえずここまで。

しばらく放っておくと追いかけるのが大変ですね。

bash で乱数を使う

bash には乱数を取得できる変数?が 用意されています。

$ echo $RANDOM
2036

繰り返し呼ぶと違う値が返ります。

$ echo $RANDOM
2036

$ echo $RANDOM
17653

man には次のようにあります。

RANDOM
  Each  time  this  parameter  is  referenced,  a  random  integer between 0 and 32767 is generated.
  The sequence of random numbers may be initialized by assigning a value to RANDOM.
  If RANDOM is  unset, it loses its special properties, even if it is subsequently reset.

範囲は 0 〜 32767 のようです。

範囲を指定したい場合は、 % で余りを求めます。

$ echo $(($RANDOM % 10))
7

printf で 0詰めにもできます。

$ VAL=`printf "%05d" $RANDOM`
$ echo $VAL
02036

RANDOM に値を設定すると、乱数の種を決めることができます。

$ RANDOM=3
$ echo $RANDOM
17653
$ echo $RANDOM
12499

$ RANDOM=3
$ echo $RANDOM
17653
$ echo $RANDOM
12499

同じ種を指定すると、同じ値が返ってきます。

find で 複数のコマンドを実行する

find コマンド取得したパスは -exec オプションを使って コマンドを実行することができます。

$ find . -mtime 3 -exec rm {} \;

-exec を複数書くと 複数のコマンドを実行することができます。

$ find . -mtime 3 -exec echo {} \; -exec rm {} \;

type コマンドの結果の "is hashed"

type コマンドで 情報を表示すると たまに "is hashed" と表示されることがあります。

$ type sl
sl is hashed (/usr/games/sl)

なんだろう?と思って調べてみると 下のサイトに「コマンドの場所へ高速にアクセスするために、よく使用するパスをキャッシュにとっていて、ハッシュテーブルがそのキャッシュ」と 書いてありました。

[参考]
typeとwhichの違いって? - OpenGroove

なるほど。

現在ハッシュテーブルにキャッシュされているコマンドは hash コマンドで確認できます。

$ hash
hits    command
   1    /usr/bin/man
   3    /usr/games/sl

hash コマンドは シェルの組み込みコマンドです。

$ type hash
hash is a shell builtin

"-r" オプションで ハッシュテーブルを削除できます。

$ hash -r

削除後に確認してみます。

$ hash
hash: hash table empty

空になっています。

ハッシュテーブルが空になった状態で もう 1 度見てみます。

$ type sl
sl is /usr/games/sl

"is hashed" が表示されなくなりました。

trap コマンドは、シェルの組み込みコマンド

この前の 補足になるのですが、 trap コマンドは シェルの組み込みコマンドです。

とりあえず which コマンド。

$ which trap

検索パスにないため 何も表示されません。

type コマンドで 情報を表示することができます。

$ type trap
trap is a shell builtin

シェルの組み込みコマンドの場合 パスではなく "shell builtin" と表示されます。

cd や alias コマンドなども同様です。

$ type cd
cd is a shell builtin

$ type alias
alias is a shell builtin

組み込みコマンドでないと "shell builtin" 以外が表示されます。

$ type find
find is /usr/bin/find

$ type ls
ls is aliased to `ls -F'

ただし、組み込みコマンドでも コマンドが alias を設定されていると "is aliased" と 表示されます。
("-t" オプションは型のみ表示)

$ type -t cd
builtin

$ alias cd="cd"

$ type -t cd
alias

$ unalias cd

$ type -t cd
builtin

ちなみに type コマンドも組み込みコマンドです。

$ type type
type is a shell builtin

シェルスクリプトの終了時に処理を実行させたい

ロックファイルや一時ファイルの削除など、シェルスクリプトの終了時に 実行したい処理というのがあります。 異常終了時に削除しておかなければならない場合など 毎回記述するのは大変です。

そんなときに使える trap というコマンドがあります。

trap コマンドは次のように処理とシグナルを指定します。

trap "echo hogehoge" EXIT

とりあえずコマンドラインで試してみます。

INT は [Ctrl]+[c] などによる割り込みのシグナルです。

$ trap "echo hogehoge" INT

[Ctrl]+[c] を押すと "echo hogehoge" を実行します。

$ trap "echo hogehoge" INT

$ ^Chogehoge

$ ^Chogehoge

解除するにはシグナルだけを指定します。

$ trap INT

$ ^C

[Ctrl]+[c] を押しても何もおこりません。

シェルスクリプトの中に 次のように書いておけば 終了時にファイルを削除してくれます。

trap "rm text1.txt" EXIT

EXIT はプロセス終了時のシグナルなので シェルスクリプトの終了をトラップしてくれます。

処理の進行状態によって削除するファイルが増えるときは 次のように再定義する方法で対応することもできます。

DELFILE="${DELFILE} test1.txt"
trap "rm ${DELFILE}" EXIT

(...処理....)

DELFILE="${DELFILE} test2.txt"
trap "rm ${DELFILE}" EXIT

現在の設定内容は "-p" オプション、 シグナルの一覧は "-l" オプションで 表示させることができます。

find コマンドの結果でファイルを削除するときのメモ

自分メモです。

find コマンドで一定時間経過したファイルを 削除するときに次のように書いたりします。

$ find /var/log/hoge -mtime +3 -exec rm {} \;

これで 3 日以上たったファイルを消してくれる・・・と思うんですが これだとディレクトリ /var/log/hoge も含まれてしまいます。

テストで /var/log/hoge を作った直後(3日経過していない場合)や このディレクトリに3日以内にファイルが格納される場合は、 抽出されないので気付きにくいのですが、 しばらく対象のディレクトリに対する更新がない状態で 上のようなコマンドを実行すると 「ディレクトリなので削除できないよ」エラーが出ます。

ですので "-type" オプションの指定を忘れないようにします。

$ find /var/log/hoge -type f -mtime +3 -exec rm {} \;

これでファイルだけが対象になります。

コマンドのエイリアスを無効にする

Redhat で作業している時にアレ?と思って 調べたのでメモとして書いておきます。

Redhat の root には 安全のためか エイリアスを使って cp コマンドの上書き確認がデフォルトになっています。

# alias
alias cp='cp -i'

この状態だと強制の "-f" オプションを付けても 確認してきます。 この確認を止めさせたいときに エイリアスを削除する以外で方法はないのかと思って 調べてみるとちゃんとありました。

エイリアスを一時的に無効にするには - @IT

コマンドの前にバックスラッシュを付けると エイリアスを無効にできるそうです。

試してみます。

$ alias cp='cp -i'
$ alias
alias cp='cp -i'

まずエイリアスを設定。

テスト用にファイルを作成。

$ touch test1.txt test2.txt

何も付けずに実行。

$ cp test1.txt test2.txt
cp: overwrite `test2.txt'?

確認してきます。

バックスラッシュを付けて実行。

$ \cp -f test1.txt test2.txt

確認してきませんでした。 エイリアスが無効になっていますね。

ネットワークインターフェース名の固定化

最近の CentOS や Fedora を入れると ネットワークのインターフェース名が eth0 とかではなく em1 みたいな名前になってしまうことがあるそうです。

em というのは ethernet-on-motherboard の略のようです。

これは biosdevname という NIC と インターフェースの対応を固定化するための 仕組みだそうで、 CentOS では、NIC(の MAC アドレス)が変わると ethX の X が変わってしまうため それを固定化するために 1段階抽象化しているようです。

[参考サイト]
ALL about Linux: CentOS 6.0 で NIC と ethX の対応を固定化する

上記サイトによると DELL のマシンは biosdevname が ON になっているため ethX ではなく emX となったようです。

RHEL も同様で、機能の有効・無効化について書いてありました。

[参考サイト]
A.3. この機能の有効化と無効化 - Red Hat Customer Portal

Raspberry Pi に tripwire を入れてみる

11/10 大阪で開催された『LPIC レベル3 303 Security 技術解説無料セミナー』で tripwire が解説されていたので、 せっかくなので Raspberry Pi に入れてみます。

tripwire は、ファイル改竄などを検出するためのツールです。

ある時点のファイルの状態をデータベースに保存しておいて 現時点の状態と比較することができます。

tripwire の設定ファイルやデータベース自体が改ざんされないように 認証用のキーを作成する必要があるのですが 最近は、インストールの流れで作成することができます。

apt-get します。

$ sudo apt-get update
$ sudo apt-get install tripwire

途中、サイトキーを作成するためのパスフレーズを求められます。

WRITE_0626_09

サイトキーは、設定ファイルを改ざんされないためのキーです。

ローカルキーを作成するためのパスフレーズを求められます。

WRITE_0626_11

ローカルキーは、データベースを改ざんされないために使用するキーです。

インストールが終了したら、データベースを初期化します。 これによって、「この時点」のファイルの情報をデータベースに保存します。

$ sudo tripwire --init
Please enter your local passphrase:

ローカルキーのパスフレーズが求められます。

データベースに保存した情報と、「現時点」のファイルの情報を 比較するには "--check" オプションを使用します。

$ sudo tripwire --check

レポートが表示されます。

レポートを再度表示するには、比較時に生成された twr ファイルを読み込みます。

$ cd /var/lib/tripwire/report
$ sudo twprint --print-report --twrfile raspberrypi-20121115-211440.twr

「比較した時点」のファイルの情報でデータベースを更新するには、 比較時に生成された twr ファイルを使って以下のようにコマンドを実行します。

$ cd /var/lib/tripwire/report
$ sudo tripwire --update -a --twrfile raspberrypi-20121115-211440.twr
Please enter your local passphrase:

データベースの更新なのでやはりローカルキーのパスフレーズが求められます。

Linux でユーザ名の変更

ユーザ名を変更するメモです。

といってもまずグループ名。 groupmod コマンドの "-n" オプションで新しい名前を指定します。

# groupmod -n NEW_NAME OLD_NAME

ユーザ名は usermod コマンドの "-l" オプションを使います。

# usermod -l NEW_NAME OLD_NAME

ホームディレクトリも変える場合は 今のホームディレクトリをリネームしておいて "-d" オプションで指定します。

# mv /home/OLD_NAME /home/NEW_NAME
# usermod -l NEW_NAME -d /home/NEW_NAME OLD_NAME

対象のユーザがログイン中の場合は変更できません。

Bash でパイプしているときの終了ステータス

Bash でパイプを使ってコマンドを実行すると 終了ステータスは、最後のコマンドの結果になります。

たとえば次のような場合

$ true | false | true

間に false (終了ステータス 1)があるのですが $? で確認すると 0 になっています。

$ true | false | true
$ echo $?
0

これは 最後の true の終了ステータスが 0 のためなのですが これでは途中にエラーがあったかどうかわかりません。

・・・と、長らく思っていたのですが、以下のサイトに方法が書いてありました。

[参考]
終了ステータス - UNIX & Linux コマンド・シェルスクリプト リファレンス

Bash の特殊変数 $PIPESTATUS に入るそうです。

$ true | false | true
$ echo ${PIPESTATUS[@]}
0 1 0

$ exit 3 | exit 12 | exit 0
$ echo ${PIPESTATUS[@]}
3 12 0

配列の形で入るので、これを使えばパイプ中のコマンドの終了ステータスも 確認することができますね。

Ubuntu10.04 で /tmp の掃除

Ubuntu10.04 には tmpwatch は入ってないし、でも再起動すると /tmp は空になるので 何か掃除しているのか?と思っていたんですが 下のサイトを見ると /etc/init/mounted-tmp.conf に設定があるようです。

[参考]
おまぬけ活動日誌(2010-07-21)

/etc/default/rcS の TMPTIME というパラメータで 削除されるまでの猶予期間を設定できるそうです。

wget でプロキシを経由しない

社内で wget を使ってるときは外部に接続するために プロキシの設定をしてあるのですが、 社内のサーバに接続するときなど プロキシを経由してほしくないってことがあります。

$ wget --no-proxy http://.....

こんな感じでプロキシを使用しないオプションをつけます。

wget で標準出力に出力する

よく忘れるのでメモです。

$ wget -q -O - http://.....

こんな感じ。

while ループ内の別プロセス

while が好きなので次のようなループを よく書きます。

ls -1 | while read FILE
do
    echo $FILE
done

これはパイプを使っているので while 以降が別プロセスになってしまいます。 (下の赤文字の部分が別プロセスです)

ls -1 | while read FILE
do
    echo $FILE
done 

なので、 ループ内で変数の値を設定しても ループの外では参照することができません。

例えば次のような場合

AAA=0 
ls -1 | while read FILE
do
    echo $FILE
    AAA=1 
done
echo $AAA 

結果として 0 が出力されます。

Linux のディストリビューション名を調べる

入ってる Linux のディストリビューションがわからない場合 Ubuntu だと次のように /etc/lsb-release を見ればわかるんですが CentOS は?と聞かれたのでメモ。

$ cat /etc/lsb-release

[参考]
インストールした Linux ディストリビューション名とバージョンを確認するには - PRiMENON:DiARY

上の参考サイトによると とりあえず /etc/issue 、 あとは /etc の下に xxxx-release みたいなファイルがあるようです。

なので次のようにしておけばよいかと。

$ cat /etc/issue /etc/*-release

sshd で IPv6 を使用しない

Ubuntu10.04 での設定メモです。
(他の環境も一緒だと思いますが・・・)

デフォルトでは IPv6 を使用する設定になっているので netstat すると次のようになります。

$ netstat -ant | grep 22
tcp    0   0 0.0.0.0:22  0.0.0.0:*    LISTEN
tcp6    0   0 :::22     :::*      LISTEN

sshd の設定ファイルを編集します。

$ sudo vi /etc/ssh/sshd_config

次の設定を追加します。

AddressFamily inet 

sshd を再起動します。

$ sudo service ssh restart

これで IPv6 で LISTEN しなくなります。

$ netstat -ant | grep 22
tcp    0   0 0.0.0.0:22  0.0.0.0:*    LISTEN

Ubuntu10.04 での wget のプロキシ設定

Ubuntu10.04 で wget コマンドをプロキシ経由で 使用するときのメモです。

環境変数で設定してやっても使えませんでした。

設定ファイルを変更します。

$ sudo vi /etc/wgetrc

次の赤字のように追加します。

# You can set the default proxies for Wget to use for http, https, 
# They will override the value in the environment.
#https_proxy = http://proxy.yoyodyne.com:18023/
#http_proxy = http://proxy.yoyodyne.com:18023/
#ftp_proxy = http://proxy.yoyodyne.com:18023/
http_proxy = http://192.168.1.200:3128/

# If you do not want to use proxy at all, set this to off.
#use_proxy = on
use_proxy = on

これでOKです。

プロキシにユーザ認証が必要な場合は 次のようにコマンドでも渡すことができます。

$ wget --proxy-user=USERNAME \
       --proxy-password=PASSWORD \
       http://www.odin.hyork.net/

Ubuntu10.04 に Webmin を入れる

Ubuntu10.04 に Webmin を入れるメモです。

Webmin は obsolete なので デフォルトでは apt-get で入れることができません。 今回は deb ファイルを持ってきて入れます。

まず deb ファイルの取得。

$ wget http://prdownloads.sourceforge.net/webadmin/webmin_1.570_all.deb

Webmin のサイトに書かれている URL だと Not Found になるので注意してください。

[参考]
Webmin http://www.webmin.com/deb.html

依存関係のあるファイルを入れます。 Ubuntu10.04 では次のような感じでした。

$ sudo apt-get install apt-show-versions
$ sudo apt-get install libapt-pkg-perl
$ sudo apt-get install libnet-ssleay-perl
$ sudo apt-get install libauthen-pam-perl
$ sudo apt-get install libio-pty-perl

最後に落としてきた deb ファイルを入れます。

$ sudo dpkg --install webmin_1.570_all.deb

これで終わりです。 10000 ポートで Webmin が起動します。

パスワードをロックする

Linux では passwd コマンドを使って ユーザのパスワードをロックすることができます。

実験用ユーザの準備。

$ sudo useradd -M -d /tmp -s /bin/bash test01
$ echo "test01:hogehoge" | sudo chpasswd

当然、今作ったユーザ test01 に切り替えることができます。

$ su - test01
パスワード:

$ id
uid=1002(test01) gid=1002(test01) 所属グループ=1002(test01)

passwd コマンドに "-l" オプションを付けて実行します。

$ sudo passwd -l test01
passwd: password expiry information changed.

これで test01 に切り替えることができなくなります。

$ su - test01
パスワード:
su: 認証失敗

ログインもできません。

ただし root からは 切り替えることができます。

$ sudo su - test01

$ id
uid=1002(test01) gid=1002(test01) 所属グループ=1002(test01)

解除するには "-u" オプションを使います。

$ sudo passwd -u test01
passwd: password expiry information changed.

usermod コマンドを使っても同じことができます。

$ sudo usermod -L test01

"-L" がロックで "-U" が解除です。大文字なので注意です。

$ sudo usermod -U test01

同じことをしているので passwd コマンドでロックして usermod コマンドで解除することもできます。

ユーザの所属するグループを表示する

ユーザのグループを表示する groups というコマンドがあります。

$ groups

ubuntu adm dialout cdrom plugdev lpadmin sambashare admin

ユーザを指定しないと 現在のユーザのグループを表示します。

ユーザを指定するとコロンで区切って表示してくれます。

$ groups root

root : root

ユーザを複数指定することもできます。

$ groups root ubuntu

root : root
ubuntu : ubuntu adm dialout cdrom plugdev lpadmin sambashare admin

なので、こんなこともできます。

$ groups `cut -f 1 -d : /etc/passwd`

root : root
daemon : daemon
bin : bin
sys : sys
sync : nogroup
games : games
man : man
lp : lp
mail : mail
news : news
uucp : uucp
proxy : proxy
www-data : www-data
backup : backup
list : list
irc : irc
gnats : gnats
nobody : nogroup
libuuid : libuuid
syslog : syslog
sshd : nogroup
landscape : landscape
ubuntu : ubuntu adm dialout cdrom plugdev lpadmin sambashare admin
postgres : postgres ssl-cert

アクセス権を指定してディレクトリを作成する

mkdir コマンドで ディレクトリを作成するときに アクセス権を同時に設定することができます。

"-m" オプションを指定します。

$ mkdir -m 700 a

見てみます。

$ ls -lF

合計 4
drwx------ 2 ubuntu ubuntu 4096 2011-11-20 23:54 a/

当然 chmod を使ってもできますが バッチ処理で実行させるときなど コマンドの成否判定が 1 回で済むので便利なときがあります。

find で確認しながらコマンド実行する

find コマンドは "-exec" でコマンドを実行することができます。

$ find . -type f -exec echo {} \;

./bbb.txt
./ccc.txt
./aaa.txt

rm コマンドなどは 実行を確認するオプション "-i" があるので それを指定するとファイルごとに確認してくれます。

$ find . -type f -exec rm -i {} \;

rm: 通常の空ファイル `./bbb.txt'を削除しますか?
rm: 通常の空ファイル `./ccc.txt'を削除しますか?
rm: 通常の空ファイル `./aaa.txt'を削除しますか?

コマンド自体に確認のオプションがない場合 find コマンドで "-exec" の代わりに "-ok" を使用します。

$ find . -type f -ok rm {} \;

< rm ... ./bbb.txt > ?
< rm ... ./ccc.txt > ?
< rm ... ./aaa.txt > ?

こんな感じになります。

打ったコマンドに自信がないときや 目でチェックしながら流したいときに便利です。

連続した空行を 1 行にまとめる

こんなテキストがあります。

aaaaaaaaaaa
bbbbbbbbb
ccccc

dddddddd



eeeeeeee
fffff


gggggg

「空行は削りたいけど、区切りなので消すわけにもいかない」 そんなときの話です。

普通に cat すると次のようになります。

$ cat test.txt

aaaaaaaaaaa
bbbbbbbbb
ccccc

dddddddd



eeeeeeee
fffff


gggggg

・・・当然ですね。

cat のオプション "-s" を指定します。

$ cat -s test.txt

aaaaaaaaaaa
bbbbbbbbb
ccccc

dddddddd

eeeeeeee
fffff

gggggg

これで、連続した空行が 1 行になりました。

ファイルを拡張子で並べる

「ウィンドウズでこうやるやつどうやるの?」というやつです。

準備。

$ touch aaa.txt
$ touch bbb.sh
$ touch ccc.log
$ touch ddd.txt
$ touch eee.cgi
$ ls -l

-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:10 aaa.txt
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:10 bbb.sh
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:11 ccc.log
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:11 ddd.txt
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:14 eee.cgi

ls で拡張子で並べるには "-X" オプションを使います。

$ ls -lX

-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:14 eee.cgi
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:11 ccc.log
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:10 bbb.sh
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:10 aaa.txt
-rw-r--r-- 1 ubuntu ubuntu 0 2011-11-18 21:11 ddd.txt

拡張子で並びました。

ディレクトリが混ざっている場合は注意が必要です。

$ mkdir eee.d
$ mkdir ggg
$ ls -l

-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:10 aaa.txt
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:10 bbb.sh
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:11 ccc.log
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:11 ddd.txt
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:14 eee.cgi
drwxr-xr-x 2 ubuntu ubuntu 4096 2011-11-18 21:15 fff.d
drwxr-xr-x 2 ubuntu ubuntu 4096 2011-11-18 21:15 ggg

"-X" オプションの無い ls ではディレクトリも名前順に並びます。

"-X" オプションを付けます。

$ ls -lX

drwxr-xr-x 2 ubuntu ubuntu 4096 2011-11-18 21:15 ggg
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:14 eee.cgi
drwxr-xr-x 2 ubuntu ubuntu 4096 2011-11-18 21:15 fff.d
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:11 ccc.log
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:10 bbb.sh
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:10 aaa.txt
-rw-r--r-- 1 ubuntu ubuntu    0 2011-11-18 21:11 ddd.txt

ディレクトリ名に "." が含まれている場合、ファイルと同じように "." より後ろを拡張子として並べてしまいます。

ファイルが新しいときだけコピーや移動する

以前「コピーや移動するときに同名のファイルを退避する」というネタを書きましたが cp コマンドや mv コマンドには、他にも便利な機能(同じことを他の方法でやろうとすると少し手間がかかる)があります。

オプション "-u" です。

このオプションは、コピーや移動するファイルのタイムスタンプが上書きするファイルよりも新しい場合のみ、処理を実行します。

準備。

$ ls -l

-rw-r--r--  1 ubuntu ubuntu    2 11/16 23:40 test1.cgi
-rw-r--r--  1 ubuntu ubuntu    4 11/16 23:42 test2.cgi ←新しい

オプション "-u" を付けてコピーします。

$ cp -u test1.cgi test2.cgi
$ ls -l

-rw-r--r-- 1 ubuntu ubuntu 2 11/16 23:40 test1.cgi
-rw-r--r-- 1 ubuntu ubuntu 4 11/16 23:42 test2.cgi ←上書きされない

コピー先の test2.cgi の方が新しいので上書きされません。

移動してみます。

$ mv -u test1.cgi test2.cgi
$ ls -l

-rw-r--r-- 1 ubuntu ubuntu 2 11/16 23:40 test1.cgi ←消えない
-rw-r--r-- 1 ubuntu ubuntu 4 11/16 23:42 test2.cgi ←上書きされない

上書きされないだけでなく、元ファイル test1.cgi も残ります。

これを以前のファイル退避のオプション "-b" と合わせると 『タイムスタンプが新しい場合のみ、コピー(移動)して、さらに退避ファイルを作成する』という動きになります。

これだけでも面白いですが これをバッチ処理に組み込むと、 タイムスタンプという別軸のフラグで 上書きする/しないを制御できるようになります。

locate コマンドで高速に検索する

locate コマンドは、コマンドの実行時にファイルを検索するのではなく 事前に作成したデータベースを使って検索するので、find コマンドよりも高速で検索することができます。

$ locate cgi

(省略)
/usr/share/doc/popularity-contest/examples/popcon-submit.cgi
/usr/share/doc/python-pexpect/examples/cgishell.cgi.gz
/var/lib/nginx/fastcgi
/var/lib/nginx/scgi

パターンマッチングも色々できます。

$ locate "*.cgi"

/usr/lib/w3m/cgi-bin/dirlist.cgi
/usr/lib/w3m/cgi-bin/multipart.cgi
/usr/lib/w3m/cgi-bin/w3mhelp.cgi
/usr/lib/w3m/cgi-bin/w3mmail.cgi
/usr/lib/w3m/cgi-bin/w3mman2html.cgi
/usr/share/doc/popularity-contest/examples/popcon-submit.cgi

検索にデータベースを使うので、作ったばかりのファイルを検索することはできません。

$ touch /var/www/test.cgi
$ locate test.cgi

データベースを更新するには updatedb コマンドを使います。

$ sudo updatedb

これで出てきます。

$ locate test.cgi

/var/www/test.cgi

データベースの更新は だいたい cron で毎日動くようになっています。 Ubuntu の場合は /etc/cron.daily にあります。

$ ls -l /etc/cron.daily/mlocate

-rwxr-xr-x 1 root root 606 2010-03-24 19:16 /etc/cron.daily/mlocate

注意しなくてはいけないのは /tmp や /var/spool などは除外の設定がされてたりして 検索しても出てこない場合があります。

Ubuntu の場合 /etc/updatedb.conf に設定されています。

$ cat /etc/updatedb.conf

PRUNE_BIND_MOUNTS="yes"
# PRUNENAMES=".git .bzr .hg .svn"
PRUNEPATHS="/tmp /var/spool /media"
PRUNEFS="NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre_lite tmpfs usbfs udf fuse.glusterfs fuse.sshfs ecryptfs fusesmb devtmpfs"

シンボリックリンクの所有者を変更する

シンボリックリンクに chown を オプションなしで実行すると リンク先の所有者が変わってしまいます。

まず準備します。

$ mkdir directory
$ ln -s directory link
$ ls -l

合計 4
drwxr-xr-x 2 ubuntu ubuntu 4096 directory
lrwxrwxrwx 1 ubuntu ubuntu    9 link -> directory

こんな感じ。

オプションなしで シンボリックリンクに chown します。

$ sudo chown root link
$ ls -l

合計 4
drwxr-xr-x 2 root   ubuntu 4096 directory
lrwxrwxrwx 1 ubuntu ubuntu    9 link -> directory

リンク先 (directory) の所有者が変わりました。

"-h" オプションを付けます。

$ sudo chown -h root link
$ ls -l

合計 4
drwxr-xr-x 2 ubuntu ubuntu 4096 directory
lrwxrwxrwx 1 root   ubuntu    9 link -> directory

これでシンボリックリンクの所有者が変わりました。

コピーや移動するときに同名のファイルを退避する

Linux で cp コマンドでコピーをしたり mv コマンドで移動するときに、 コピー先や移動先に同名のファイルがあると 通常は上書きしてしまいます。

この 2 つのコマンドには 同名のファイルがあったときに そのファイルを退避させるオプションがあります。

次のように a/ と b/ のどちらにも c.txt があるとします。

$ ls a/

c.txt

$ ls b/

c.txt

通常の cp コマンドだと上書きするだけです。

$ cp a/c.txt b/

$ ls b/

c.txt ←上書きされたファイル

-b オプションを付けます。

$ cp -b a/c.txt b/

$ ls b/

c.txt c.txt~

このように c.txt~ ができます。
ちょっとわかりにくいですが c.txt が上書きされたファイルで 元々あった c.txt が c.txt~ になっています。

ちなみに Linux では ファイル名の最後に ~ が付いている場合 退避したファイルや一時ファイルである場合が多いです。

保存されるのは 1 世代だけなので もう一度コピーをすると 今の c.txt が c.txt~ になります。

バッチ処理なんかで 1 世代だけ残しておけば良いときなど 他の方法でやるよりもずっと手軽です。

LPIC Level3 Specialty 304 を受けてきました

先週、前から受けようと思ってた LPIC Level3 Specialty 304 を受けてきました。

ギリギリでしたが、いつものように簡単な受験体験記を書きました。

シェルの組み込みコマンドの一覧を表示する

bash の組み込みコマンドの一覧を表示するには 組み込みコマンドの help を使います。

$ help

GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu)
These shell commands are defined internally.  Type `help' to see this list.
Type `help name' to find out more about the function `name'.
Use `info bash' to find out more about the shell in general.
Use `man -k' or `info' to find out more about commands not in this list.

A star (*) next to a name means that the command is disabled.

 job_spec [&]                            history [-c] [-d offset] [n] or hist>
 (( expression ))                        if COMMANDS; then COMMANDS; [ elif C>
 . filename [arguments]                  jobs [-lnprs] [jobspec ...] or jobs >
 :                                       kill [-s sigspec | -n signum | -sigs>
 [ arg... ]                              let arg [arg ...]
 [[ expression ]]                        local [option] name[=value] ...

組み込みコマンドの詳細を見るには help の後にコマンドを続けて書きます。

$ help bg

bg: bg [job_spec ...]
    Move jobs to the background.

    Place the jobs identified by each JOB_SPEC in the background, as if they
    had been started with `&'.  If JOB_SPEC is not present, the shell's notion
    of the current job is used.

    Exit Status:
    Returns success unless job control is not enabled or an error occurs.

tcsh や csh の組み込みコマンドの一覧を表示するには builtins コマンドを使います。

> builtins

:          @          alias      alloc      bg         bindkey    break
breaksw    builtins   case       cd         chdir      complete   continue
default    dirs       echo       echotc     else       end        endif
endsw      eval       exec       exit       fg         filetest   foreach

所有者でファイルを探す

ファイルを探すときに便利なコマンド find には 所有者を指定して探すオプションがあります。

$ find . -user root

これで所有者が root になっているファイルを探すことができます。

グループを指定することもできます。

$ find . -group adm

ユーザとグループを両方指定することもできます。

$ find . -user root -group adm

ユーザを削除するときに そのユーザのファイルが残っていないか 探すときに便利です。

wget で途中で止まった時に再開する

wget でダウンロードしているときに 途中で止まることがあります。

$ wget http://example.com/images/cdrom.iso

--2011-11-01 22:38:49--  http://example.com/images/cdrom.iso
Connecting to 192.168.3.1:3128... connected.
Proxy request sent, awaiting response... 200 OK
Length: 717225984 (684M) [application/octet-stream]
Saving to: `cdrom.iso'

99% [========================================> ] 714,444,862 --.-K/s  eta 2s

そんなときは -c オプションを付けて実行します。

$ wget -c http://example.com/images/cdrom.iso

--2011-11-01 22:49:16--  http://example.com/images/cdrom.iso
Connecting to 192.168.3.1:3128... connected.
Proxy request sent, awaiting response... 206 Partial Content
Length: 717225984 (684M), 2781122 (2.7M) remaining [application/octet-stream]
Saving to: `cdrom.iso'

100%[+++++++++++++++++++++++++++++++++++++++>] 717,225,984 1.00M/s   in 2.6s

2011-11-01 22:49:18 (1.00 MB/s) - `cdrom.iso' saved [717225984/717225984]

これで前の続きからダウンロードすることができます。

連続した同じ文字を 1 文字にする tr コマンド

tr コマンドには 連続した同じ文字を 1 文字にする -s というオプションがあります。

例えば次のようにスペースが続いている場合

abcde      fghij

tr コマンドを使えば簡単にスペースを 1 つにすることができます。

abcde fghij

次のように指定します。

$ echo "abcde      fghij" | tr -s [:space:]

abcde fghij

[:space:]は、スペースやタブなどを示す文字クラスです。

ls の出力も簡単に加工できます。 (意味はないですが)

$ ls -ltr | head | tr -s [:space:]

total 9498
drwx------ 2 root root 48 1970-01-01 09:00 gconfd-root
drwx---rwx 2 root hpusers 48 2006-09-09 16:03 mmcache
drwxr-xr-x 2 apache apache 48 2006-09-09 16:11 fcgi
drwx------ 2 root root 120 2006-09-27 12:15 YaST2-06402-9kTlAk
drwxr-xr-x 4 root root 96 2007-06-03 17:21 pear
drwx------ 3 root root 80 2007-07-06 19:09 spamd-4708-init
drwx------ 3 root root 80 2007-07-06 22:28 spamd-4426-init
drwx------ 2 root root 120 2009-01-15 09:46 YaST2-04662-jtfPyg
srwxrwxrwx 1 mysql mysql 0 2010-03-04 08:29 mysql.sock

連続する「同じ文字」を 1 つにするだけなので [:alpha:] を 指定すると次のようになります。

$ echo "aaabbccddaaa" | tr -s [:alpha:]

abcda

思いがけないときに役に立つことがあります。

apt-get でパッケージを指定してアップデートする

apt-get の upgrade は インストール済みのパッケージを全てアップデートします。

apt-get update は パッケージのアップデートではなく、 パッケージリストのアップデートです。 指定したパッケージをアップデートするには ややこしいですが install を指定します。

インストール済みの apache2 をアップデートしてみます。

$ sudo apt-get install apache2
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  postgresql-doc php5-ldap postgresql-doc-8.4
Use 'apt-get autoremove' to remove them.
The following extra packages will be installed:
  apache2-mpm-worker apache2.2-bin apache2.2-common php5-cli php5-common php5-gd php5-ldap php5-pgsql
Suggested packages:
  apache2-doc apache2-suexec apache2-suexec-custom php-pear php5-suhosin
The following packages will be REMOVED:
  apache2-mpm-prefork libapache2-mod-php5 php5 phpldapadmin phppgadmin
The following NEW packages will be installed:
  apache2-mpm-worker
The following packages will be upgraded:
  apache2 apache2.2-bin apache2.2-common php5-cli php5-common php5-gd php5-ldap php5-pgsql
8 upgraded, 1 newly installed, 5 to remove and 90 not upgraded.
Need to get 6,609kB of archives.
After this operation, 20.7MB disk space will be freed.
Do you want to continue [Y/n]? 

このように upgraded パッケージ数が表示されます。

Bash のチルダ展開

Bash では次のようにチルダ (~) で カレントユーザのホームディレクトリに移動することができます。

$ cd ~

チルダに続けてユーザ名を指定すると そのユーザのホームディレクトリに展開されます。

$ cat ~hogehoge/.bashrc

他のユーザのホームディレクトリを調べなくても ホームディレクトリを指定することができます。

exim4 の再設定

Ubuntu は標準のメールサーバとして exim4 を入れることができます。 この exim4 は次のファイルに 設定内容を格納しています。

/etc/exim4/update-exim4.conf.conf (typo じゃないです)
/etc/mailname

/etc/exim4/update-exim4.conf.conf には exim4 の設定内容が、 /etc/mailname には メール送信時に送信者の @ の後ろに付けるドメイン名が入ります。

$ cat /etc/mailname

example.com

ファイルの内容を修正した場合 dpkg-reconfigure を実行する以外に update-exim4.conf というスクリプトの実行で再設定することができます。

$ sudo update-exim4.conf

末尾に .conf と付いていますがスクリプトファイルです。 exim4 の設定を更新するので update-exim4.conf で その設定ファイルなので update-exim4.conf.conf なんだと思います。

ファイルのステータスを表示するコマンド

Linux には ファイルのステータスを表示する stat というコマンドがあります。

$ stat /var/log/user.log

  File: `/var/log/user.log'
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fc01h/64513d    Inode: 157223      Links: 1
Access: (0640/-rw-r-----)  Uid: (  101/  syslog)   Gid: (    4/     adm)
Access: 2011-02-03 10:18:03.000000000 +0900
Modify: 2011-02-03 10:18:03.000000000 +0900
Change: 2011-06-28 18:30:39.000000000 +0900

ファイルの最終アクセス日時まで取得できます。

ちなみに Access が最終アクセス日時、 Modify が最終変更日時、 Change が権限などの最終変更日時です。

ファイルの情報を取得するコマンドに file というのもありますが こちらはファイルタイプを表示します。

$ file /var/log/boot

/var/log/boot: ASCII text

$ file /usr/sbin/apache2

/usr/sbin/apache2: symbolic link to `../lib/apache2/mpm-prefork/apache2'

$ file /usr/lib/apache2/mpm-prefork/apache2

/usr/lib/apache2/mpm-prefork/apache2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

$ file /etc/init.d/apache2

/etc/init.d/apache2: POSIX shell script text executable

sudo のタイムアウトまでの時間を長くする

Ubuntu では root を使わずに sudo することになるのですが デフォルトでは 1 度 sudo してから 5 分 sudo しないと 再度パスワードの入力を求められます。

通常はそれで良いのですが まとまった作業をしているときなどは もう少し長くしたいときがあります。

sudo の設定は /etc/sudoers を visudo コマンドで編集するのですが Ubuntu10.04 だとエディタは nano が起動するので env で環境変数を変えて vi にします。
(これは私が nano を使えないからです・・・)

$ sudo env EDITOR=/usr/bin/vi visudo

元々ある Defaults の下辺りに追加します。

Defaults        env_reset                ←既存
Defaults        timestamp_timeout = 20   ←追加

これでタイムアウトまでの時間が 20 分になりました。

Defaults はカンマで区切って 1 行で書いても OK です。

Defaults        env_reset, timestamp_timeout = 20

Bash の 変数展開

Bash の 変数展開には結構種類があります。

よく使うのは次の「文字列の末尾から最短マッチさせて削除」で 「ファイルの拡張子」の削除です。

$ ls *.txt | while read FILE
> do
>     echo ${FILE%.txt}
> done

"${FILE%.txt}" の部分が変数展開です。 これで ls コマンドで .txt のファイルだけを取得して ファイル名から .txt を削除した部分を出力することができます。

変数展開には次のようなものがあります。

${変数:+文字列} 変数が存在し空でなければその文字列、それ以外なら空
${変数:-文字列} 変数が存在し空でなければその値、それ以外なら文字列
${変数:=文字列} 変数が存在し空でなければその値、それ以外なら変数に文字列を設定
${変数:?文字列} 変数が存在し空でなければその値、それ以外なら文字列を出力して終了
${変数:位置} 位置から末尾までの部分文字列
${変数:位置:長さ} 位置から長さ分の部分文字列
${変数#パターン} 変数の先頭がパターンマッチした場合、最短マッチ部分を削除
${変数##パターン} 変数の先頭がパターンマッチした場合、最長マッチ部分を削除
${変数%パターン} 変数の末尾がパターンマッチした場合、最短マッチ部分を削除
${変数%%パターン} 変数の末尾がパターンマッチした場合、最長マッチ部分を削除
${変数/パターン/文字列} 最初にパターンマッチした部分を文字列で置換
${変数//パターン/文字列} パターンマッチしたすべての部分を文字列で置換

Bash だけでも色々なことができます。

sudo でリダイレクト

sudo でリダイレクトする場合、次のように書くと、リダイレクト先のファイルの権限は sudo したユーザになります。

$ sudo echo aaa > test.txt
                ~~~~~~~~~~ ←この部分は sudo とは無関係

リダイレクトごと sudo した権限で実行したい場合は 次のように書きます。

$ sudo sh -c "echo aaa > test.txt"

Bash の組み込みコマンドと外部コマンド

Bash には cd のように Bash 自体に組み込まれているコマンドがあります。 組み込みコマンドか外部コマンドかは type というコマンドで確認できます。

$ type cd
cd is a shell builtin

組み込みコマンドは "builtin" と出力されます。

$ type ftp
ftp is /usr/bin/ftp

外部コマンドは パスが出力されます。

$ type ls
ls is aliased to `ls --color=auto'

エイリアスはこのように出力されます。

エイリアス元を調べるには "-a" オプションを付けます。

$ type -a ls
ls is aliased to `ls --color=auto'
ls is /bin/ls

type コマンドも Bash の組み込みコマンドです。

$ type -t type
builtin

Bash の組み込みコマンド pushd

bash には pushd というコマンドがあります。

pushd コマンドは cd コマンドと同じように ディレクトリを移動することができます。

cd コマンドとの違いは 移動したディレクトリをディレクトリスタックに 積んで(保存して)くれます。 "push directory" なワケです。

ディレクトリスタックの内容は dirs コマンドで見ることができます。 初期状態では、ホームディレクトリが ディレクトリスタックに積まれています。

~:$ dirs
~

実際に使ってみます。 pushd コマンドは、実行後に現在のディレクトリスタックを 表示してくれます。

~:$ pushd /var
/var ~

/var:$ pushd /home
/home /var ~

/home:$

ディレクトリスタックから取り出すには popd コマンドを使います。

/home:$ popd
/var ~

/var:$

このままでも良いのですが 私は function を使って既存の cd コマンドを 置き換えて使用しています。

function cd {
    case $1 in
        "")
            pushd $HOME > /dev/null
            ;;
        "-")
            [ `dirs | wc -w` -gt 1 ] && popd > /dev/null
            ;;
        *)
            [ -d $1 ] && pushd $1 > /dev/null
            ;;
    esac
}

引数を指定しなければホームディレクトリに、ディレクトリを 指定した場合は移動してディレクトリスタックに積みます。 ハイフン "-" を指定した場合はディレクトリスタックから取り出します。

tac コマンドと rev コマンドの使い道

以前紹介した tac コマンドrev コマンドの 使い道を探していたのですが、素晴らしいネタが書いてあるサイトがありました。

【参考サイト】
しげふみメモ : Linuxのrevコマンドで文字を逆に並べる

ソートや文字の切り出しは考えていたのですが banner コマンドを使ったネタは想定外でした。

banner は 次のような出力ができるコマンドです。

$ banner Linux

#        ###  #     #  #     #  #     #
#         #   ##    #  #     #   #   #
#         #   # #   #  #     #    # #
#         #   #  #  #  #     #     #
#         #   #   # #  #     #    # #
#         #   #    ##  #     #   #   #
#######  ###  #     #   #####   #     #

これを tac コマンドに通すと上下が逆になります。

$ banner Linux | tac

#######  ###  #     #   #####   #     #
#         #   #    ##  #     #   #   #
#         #   #   # #  #     #    # #
#         #   #  #  #  #     #     #
#         #   # #   #  #     #    # #
#         #   ##    #  #     #   #   #
#        ###  #     #  #     #  #     #

rec コマンドに通すと左右が逆になります。

$ banner Linux | rev

  #     #  #     #  #     #  ###        #
   #   #   #     #  #    ##   #         #
    # #    #     #  #   # #   #         #
     #     #     #  #  #  #   #         #
    # #    #     #  # #   #   #         #
   #   #   #     #  ##    #   #         #
  #     #   #####   #     #  ###  #######

2 つのコマンドに通すと 180 度回転させることができます。

$ banner Linux | rev | tac

  #     #   #####   #     #  ###  #######
   #   #   #     #  ##    #   #         #
    # #    #     #  # #   #   #         #
     #     #     #  #  #  #   #         #
    # #    #     #  #   # #   #         #
   #   #   #     #  #    ##   #         #
  #     #  #     #  #     #  ###        # 

アイデア次第だと感心させられます。

文字列を反転させる rev コマンド

以前書いた tac コマンドは 行を反転さるコマンドでした。 そこで、行内の文字列を反転するコマンドも無いかと探してみたら rev というコマンドがありました。

次のテキスト( test.txt )を処理します。

1 2 3
4 5 6
7 8 9

rev コマンドで出力します。

$ rev test.txt

3 2 1
6 5 4
9 8 7

行の順番はそのままですが、行内で文字が反転しています。

標準入力を受けることもできます。

$ cat test.txt | rev 

3 2 1
6 5 4
9 8 7

tac と合わせて使うと全て反転させることができます。

$ tac test.txt | rev

9 8 7
6 5 4
3 2 1

ソートに使うくらいで、他に「これぞ!」という使い道は思いつきませんが いつか必要になるときのために、こういったコマンドが用意されているのは安心ですね。

tac コマンドとは

Linux には tac というコマンドがあります。

資格の学校とは全く関係はありません。
「 cat コマンドの逆」ということで tac です。

$ cat test.txt

abc
123
efg
456
$ tac test.txt

456
efg
123
abc

このように cat とは 逆順の出力になります。

デフォルトでは、区切り文字が改行になっているので 行単位で逆になります。 他のコマンドで行を逆にするのは結構大変なので 覚えておくと役に立つときがあるんじゃないでしょうか。

他にも if 〜 fi、case 〜 esac といったように 「逆の文字列」で何かを表現することがありますが、 テキストならではの発想で面白と思います。

newgrp コマンドでグループを変更する

Linux に newgrp というグループを変更するコマンドがあります。

このコマンド、設定ファイルなどを変更するコマンドではなく su コマンドのように一時的に状態を変更するコマンドです。

まずグループについてですが、 Linux のユーザは 次の例のように複数のグループに属することができます。

$ id

uid=501(hoge) gid=502(develop) groups=497(manage),502(develop)

id コマンドで出力される groups は、補助グループと呼ばれる ユーザが属しているグループです。
gid の方は、一次グループと呼ばれ、ファイルを作成したときは このグループの権限が設定されます。

次のコマンドで、一次グループを変更することができます。

$ newgrp manage
$ id

uid=501(hoge) gid=497(manage) groups=497(manage),502(develop)

ファイルを作成するとグループが変わっているのがわかります。

$ ls -l

-rw-r--r--  1 hoge develop         0 2010-06-01 22:55 a.txt
-rw-r--r--  1 hoge manage          0 2010-06-01 22:56 b.txt

次の場合、newgrp するときにパスワードの入力が求められます。

  • ユーザにはパスワードがなくグループにはある
  • ユーザがグループのメンバーではなくグループにパスワードがある

su コマンド同様、root の場合はパスワードの入力が不要です。

newgrp すると su と同様に新しいシェルが起動します。

$ pstree

init┬
    ├
    ├sshd─sshd─bash─pstree

$ newgrp manage
$ pstree

init┬
    ├
    ├sshd─sshd─bash─bash─pstree

su コマンドと同様に環境を初期化するには "-" を指定します。

$ newgrp - manage

また、sg という newgrp とほぼ同機能のコマンドもあります。
newgrp との大きな違いとして、su コマンドと同様に "-c" オプションでコマンドを実行することができます。

$ sg --help
usage: sg group [[-c] command ]

ちなみに、sg コマンドは newgrp のシンボリックリンクのようです。

$ ls -l /usr/bin/newgrp /usr/bin/sg

-rwsr-xr-x  1 root root 27080 Mar  8  2004 /usr/bin/newgrp
lrwxrwxrwx  1 root root     6 Jan 20  2004 /usr/bin/sg -> newgrp

Linux の join コマンドをテキストの抽出に使ってみる

join というコマンドがあります。

配列を文字列に結合する join もありますが データベースを使ってる人ならテーブルの結合を 思い浮かべるのではないでしょうか。 そちらのイメージです。

まずは paste コマンドのおさらい。
次の 2 つのファイル sugaku.txt と kokugo.txt を処理します。

ito     98      1
kameda  83      2
tanaka  71      3
wada    32      4
ito     91      2
tanaka  100     1
wada    45      3

数学と国語のテスト結果で、「名前、点数、順位」が テキストファイルに書かれています。

join します。

$ join sugaku.txt kokugo.txt

ito     98      1       91      2
tanaka  71      3       100     1
wada    32      4       45      3
        ~~~~|~~~~       ~~~~|~~~~
    sugaku.txtの内容   kokugo.txtの内容

2 つのファイルが 1 つにまとまりました。
オプションなしの場合、1 列目の値を使って結合します。

kokugo.txt に kameda の行はなかったため除外されました。 データベーステーブルの内部結合と同じです。

結合条件となる列を変えることもできますが、 あらかじめソートされている必要があります。 順位で結合するために kokugo.txt を順位の列でソートして kokugo2.txt を作ります。
( sugaku.txt は順位の列も昇順になっているのでソート不要)

$ sort +2 -n kokugo.txt > kokugo2.txt

$ cat kokugo2.txt

tanaka  100     1
ito     91      2
wada    45      3

3 列目の順位の列で join します。

$ join -1 3 -2 3 sugaku.txt kokugo2.txt

1       ito     98      tanaka  100
2       kameda  83      ito     91
3       tanaka  71      wada    45
        ~~~~|~~~~~      ~~~~|~~~~~~
    sugaku.txtの内容   kokugo2.txtの内容

"-1" は 1 つ目のファイルの結合条件となる列を指定します。 "-2" は 2 つ目のファイルの結合条件となる列です。

結合条件となる列は 先頭に出力されます。

ファイルの 1 つを標準入力から受けることもできます。 その場合、ファイルの指定の変わりにハイフン "-" を指定します。

$ cat sugaku.txt | join -1 3 -2 3 - kokugo2.txt

1       ito     98      tanaka  100
2       kameda  83      ito     91
3       tanaka  71      wada    45

これを利用して 標準入力から受けたコマンドの出力結果のテキストを 抽出してみたいと思います。

ls の結果が次のようになるとします。

$ ls -l

-rw-r--r--  1 hoge hoge    54 May 31 04:12 a.txt
-rw-r--r--  1 hoge hoge    54 May 31 04:12 b.txt
-rw-r--r--  1 hoge hoge    54 May 31 04:12 c.txt
-rw-r--r--  1 hoge hoge    54 May 31 04:12 d.txt

まず次のようなテキスト( where.txt )を用意します。

a.txt
b.txt

join します。

$ ls -l | join -1 9 - where.txt

a.txt -rw-r--r--  1 hoge hoge    54 May 31 04:12
b.txt -rw-r--r--  1 hoge hoge    54 May 31 04:12

a.txt と b.txt だけ抽出できました。

列を指定して抽出できるので抽出条件が他の列に混ざっていても 大丈夫という利点があります。

Linux の paste コマンドで カンマ区切りのデータを作る

paste というコマンドがあります。

2 つのファイルを行ごとに連結するするコマンドなのですが 何か“ちょっと違う”使い道がないか考えていました。 今回は、このコマンドでカンマ区切りのデータを作ってみたいと思います。

まずは paste コマンドのおさらい。
次の 2 つのファイル user.txt と color.txt を処理します。

sato
suzuki
watanabe
yamada
red
black
white

この 2 つのファイルをを列ごとに連結します。

$ paste user.txt color.txt

sato    red
suzuki  black
watanabe        white
yamada

デフォルトでは、列は TAB で連結されます。 color.txt が 1 行少ないので 最後の行は "yamada" のみ出力されます。

区切りを変えることもできます。

$ paste -d "=" user.txt color.txt

sato=red
suzuki=black
watanabe=white
yamada=

"-s" オプションを付けると、ファイルごとに 1 行になります。

$ paste -s user.txt color.txt

sato    suzuki  watanabe        yamada
red     black   white

標準入力を受けることもできます。

$ cat color.txt | paste -s

red black white

これを利用することで、 標準入力を受けて カンマ区切りで出力することができます。

$ ls | paste -s -d ","

a.txt,b.txt,c.txt,d.txt

Linux の sort コマンドでソートする列を指定する

Linux の sort コマンドはソートする列を指定することができます。

次のテキストファイル( score.txt )を処理してみます。

yamada  90
tanaka  100
sato    64
suzuki  93

オプションなしのソートだと次のようになります。

$ sort score.txt

sato    64
suzuki  93
tanaka  100
yamada  90

2 列目でソートします。

$ sort -k 2,2 score.txt

tanaka  100
sato    64
yamada  90
suzuki  93

"2,2" という指定は 「 2 列目から 2 列目まで」という意味で 2 列目のみを使ってソートするときの指定です。 "2" だけを指定すると 「 2 列目から最後まで」という意味になります。

数字が“数値”として認識されていないので ソート順が変です。 "-n" オプションで、数値を“数値”と認識させます。

$ sort -k 2,2 -n score.txt

sato    64
yamada  90
suzuki  93
tanaka  100

ついでに 値の大きい順(降順)にします。

$ sort -k 2,2 -n -r score.txt

tanaka  100
suzuki  93
yamada  90
sato    64

ソートできました。

CSV ファイルのように列の区切り文字が空白でないときは 次のように "-t" オプションで区切り文字を指定できます。

$ sort -k 2,2 -n -r -t "," score.txt

awk コマンドでヘッダーやフッターを出力する

テキストを扱う awk というコマンドがあります。

このコマンド、かなり便利なのですが、 できることが多過ぎるので覚えるのは結構大変だったりします。 「基本的な使い方はわかるけど・・・」という人も多いのではないでしょうか。 自分へのメモの意味も含めて機能を紹介したいと思います。

次のテキストを処理します。( test.txt )

1 2 3
4 5 6
7 8 9
10 11 12

まず基本的な使い方です。

$ cat test.txt | awk '{print $2}'

2
5
8
11

awk の書式で {} の前にはパターンを書くことができます。

{} の前に何も書かないのは、全ての行にマッチ(全ての行に対して処理をする)という指定になります。 (これについてはまた別の機会に詳しく書きたいと思います)

今回は、BEGIN と END という特殊な指定を使いますが、BEGIN は「最初の行の前」にマッチする指定で END は「最後の行の後」にマッチする指定です。これを使ってヘッダーやフッターを出力します。

次のような指定ができます。

$ cat test.txt | \
>     awk 'BEGIN {print "-- header --"} END {print "-- footer --"}'

-- header --
-- footer --

BEGIN と END の指定しかないので、ヘッダーとフッターのみが出力されました。

プログラムの部分が長くなるので、別ファイル( prog.txt )に指定して 引数で読み込むようにします。

次のような指定をしてみます。

BEGIN {
    print " f1 | f2 | f3 ";
    print "----+----+----";
}
{
    printf("%4d|%4d|%4d\n", $1, $2, $3);
}

BEGIN (前半)と 全ての行にマッチする指定(後半)をしました。

次のような結果になります。

$ cat test.txt | awk -f prog.txt

 f1 | f2 | f3
----+----+----
   1|   2|   3
   4|   5|   6
   7|   8|   9
  10|  11|  12

ヘッダーと本体が出力されました。

次はフッターに合計行を出したいと思います。
awk は計算もできます。

BEGIN {
    print " f1 | f2 | f3 ";
    print "----+----+----";
    f1=0;
    f2=0;
    f3=0;
}
{
    printf("%4d|%4d|%4d\n", $1, $2, $3);
    f1=f1+$1;
    f2=f2+$2;
    f3=f3+$3;
}
END {
    print "----+----+----";
    printf("%4d|%4d|%4d\n", f1, f2, f3);
}

初期化処理は BEGIN に書くことができます。

次のような結果になります。

$ cat test.txt | awk -f prog.txt

 f1 | f2 | f3
----+----+----
   1|   2|   3
   4|   5|   6
   7|   8|   9
  10|  11|  12
----+----+----
  22|  26|  30

合計行を出力することができました。

ちょっとしたときにサッと書けるとカッコ良いですね。

vi が怖いときの view コマンド

「ファイルを vi で開くと間違って上書き保存してしまうのが怖い」と 言われたことがあります。 vi を終了する際に ":wq" と打ってしまうそうなのですが これでは確かに更新日時が変わってしまいます。

そこで view というコマンドがあります。

$ view a.txt

このコマンドはファイルを vi の読み取りモードの "-R" オプションを付けた状態で開いてくれます。

この状態であれば ":w" で保存しようとしても「読み取り専用です」とエラーになります。 ただし ":w!" のような強制保存はできてしまうので注意して下さい。

vi の操作で保存だけしたくない、というときに安心なコマンドです。

Linux の find コマンドを使ってみる

find コマンドは 色々な条件でファイルを探してリストを出力してくれる 便利なコマンドです。 しかし、全てのオプションは とても覚えきれません。 そこで今回は“最低限これだけは”という内容に絞って 紹介したいと思います。

パスの指定

まずはパスの指定です。
find コマンドは指定したパス以下を検索します。

パスの指定をしないと カレントディレクトリを指定したことになります。 また、パスの指定によって結果の表示も変わってしまいます。 (オプションで変更することはできますが、今回は割愛します)

例えば、パスを「相対パス」で指定した場合、次のように 結果の出力も相対パスになります。

$ find ./hogehoge

./hogehoge
./hogehoge/a.txt
./hogehoge/b.txt

「絶対パス」で指定すれば 結果も絶対パスになります。

$ find /home/hogehoge

/home/hogehoge
/home/hogehoge/a.txt
/home/hogehoge/b.txt

抽出条件の指定

次に抽出条件です。
多くの場合、「ファイル名」か「更新日」になると思います。

ファイル名は "-name" オプションで 条件を指定できます。

$ find . -name "*.txt"

./hogehoge/a.txt
./hogehoge/b.txt

この例では "*.txt" にマッチするファイルのみを抽出しました。 マッチングには "*" や "?" を使うことができます。

ただし、マッチングの対象になるのはパスの最後のエントリー名(上の例では "a.txt" や "b.txt")のみで、途中のパスは対象になりません。 ディレクトリ自体が最後のエントリーの場合は、そのディレクトリ名が マッチングの対象になります。

$ find . -name "hoge*"

./hogehoge

更新日は "-mtime" オプションで ファイルが更新されてからの時間が指定できます。

$ find . -mtime +3

./hogehoge/a.txt

値の指定は、プラス "+" の場合 〜日以上前(上の例の場合 3 日以上前)で マイナス "-" の場合 〜日以内となります。

$ find . -mtime -3

./hogehoge
./hogehoge/b.txt

処理の実行

取得したパスに対して処理を行ないたいときがあります。

例えば「 3 日以上前の zip ファイルを削除する」といった場合ですが 対象の取得は 次のようなオプションで指定できます。

$ find /var/backup -mtime +3 -name "*.zip"

/var/backup/a.zip
/var/backup/b.zip

これに対して処理をするわけですから イメージとしてはパイプを使って 次のようになります。

$ find /var/backup -mtime +3 -name "*.zip" | while read path
> do
>   rm $path
> done

同じ動作を "-exec" オプションで指定することができます。

$ find /var/backup -mtime +3 -name "*.zip" -exec rm {} \;

"-exec" 以降、"\;" までがコマンドになります。
"{}" の部分に取得されたパスが入ります。

その他のオプション

その他のオプションでオススメなのは "-ls" です。 このオプションを付けると結果の出力を ls コマンドと同じような 形にしてくれます。

$ find . -ls

10767946 12 drwxr-xr-x 1 u g 4096 11月 20 2009 ./hogehoge
10768066  4 -rw-r--r-- 1 u g  512 11月 15 2009 ./hogehoge/a.txt
10768041  4 -rw-r--r-- 1 u g  512 11月 20 2009 ./hogehoge/b.txt

他にも色々なオプションがありますが 「どうしても使わないとならない」という場面以外では 他のコマンドと組み合わせて使う方が シンプルで見やすくなると思います。

Bash で計算してみる

sh などでは変数を計算に使用できないため 次のように expr コマンドを使って計算します。

$ echo `expr 3 + 22`

25
$ A=15
$ B=`expr $A - 12`
$ echo $B

3

Bash では $ + 二重カッコ "$(( ))" で計算ができます。

$ echo $((3 + 22))

25
$ A=15
$ B=$(($A - 12))
$ echo $B

3

カッコ内の変数は $ を省略できます。

$ A=15
$ B=$((A - 12))
$ echo $B

3

計算するだけの場合、カッコの前の $ は不要です。

$ A=15
$ ((B = A * 2))
$ echo $B

30

Linux の echo で改行させない

使用した例は、ちょこちょこ出していたのですが メモ代わりに単独でネタにしておきます。

オプションを付けずに echo を使うと 行末に改行が付きます。

$ echo ABC; echo XYZ
ABC
XYZ

"-n" オプションを付けることで 改行なしにできます。

$ echo -n ABC; echo -n XYZ
ABCXYZ

cal コマンドで月曜日を先頭にする

cal コマンドで カレンダーを表示することができます。

$ cal

      5月 2010
日 月 火 水 木 金 土
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

$ cal 9 2010

      9月 2010
日 月 火 水 木 金 土
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

.bash_profile に入れてログイン時にカレンダーを表示させたり ちょっと曜日を知りたい時に使えるコマンドです。

ただ、私の場合 カレンダーは月曜日が先頭なのが嬉しいのですが 最近の cal はそんなオプション "-m" もあるようです。

$ cal -m

      5月 2010
月 火 水 木 金 土 日
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

他にも年単位の表示や...

$ cal -y

                             2010

        1月                   2月                   3月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
                1  2      1  2  3  4  5  6      1  2  3  4  5  6
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   7  8  9 10 11 12 13
10 11 12 13 14 15 16  14 15 16 17 18 19 20  14 15 16 17 18 19 20
17 18 19 20 21 22 23  21 22 23 24 25 26 27  21 22 23 24 25 26 27
24 25 26 27 28 29 30  28                    28 29 30 31
31
        4月                   5月                   6月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
             1  2  3                     1         1  2  3  4  5
 4  5  6  7  8  9 10   2  3  4  5  6  7  8   6  7  8  9 10 11 12
11 12 13 14 15 16 17   9 10 11 12 13 14 15  13 14 15 16 17 18 19
18 19 20 21 22 23 24  16 17 18 19 20 21 22  20 21 22 23 24 25 26
25 26 27 28 29 30     23 24 25 26 27 28 29  27 28 29 30
                      30 31
        7月                   8月                   9月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
             1  2  3   1  2  3  4  5  6  7            1  2  3  4
 4  5  6  7  8  9 10   8  9 10 11 12 13 14   5  6  7  8  9 10 11
11 12 13 14 15 16 17  15 16 17 18 19 20 21  12 13 14 15 16 17 18
18 19 20 21 22 23 24  22 23 24 25 26 27 28  19 20 21 22 23 24 25
25 26 27 28 29 30 31  29 30 31              26 27 28 29 30

        10月                  11月                  12月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
                1  2      1  2  3  4  5  6            1  2  3  4
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   5  6  7  8  9 10 11
10 11 12 13 14 15 16  14 15 16 17 18 19 20  12 13 14 15 16 17 18
17 18 19 20 21 22 23  21 22 23 24 25 26 27  19 20 21 22 23 24 25
24 25 26 27 28 29 30  28 29 30              26 27 28 29 30 31
31

前後 1 ヵ月ずつの表示もあります。

$ cal -3

      4月 2010              5月 2010              6月 2010
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
             1  2  3                     1         1  2  3  4  5
 4  5  6  7  8  9 10   2  3  4  5  6  7  8   6  7  8  9 10 11 12
11 12 13 14 15 16 17   9 10 11 12 13 14 15  13 14 15 16 17 18 19
18 19 20 21 22 23 24  16 17 18 19 20 21 22  20 21 22 23 24 25 26
25 26 27 28 29 30     23 24 25 26 27 28 29  27 28 29 30
                      30 31

Linux で cd する前のディレクトリに戻る

ものすごい小ネタですが 知っていると便利です。

cd コマンドはディレクトリを移動することができます。

~:$ cd /var/log/apache2

/var/log/apache2:$ cd /etc/apache2/conf.d

/etc/apache2/conf.d:$

1 つ前のディレクトリに戻りたくなったときは 次のように パスの代わりにハイフン "-" を指定します。

/etc/apache2/conf.d:$ cd -

/var/log/apache2:$

履歴で持っているわけではないので 繰り返しハイフンを指定しても 2 つのディレクトリを行き来するだけです。

/etc/apache2/conf.d:$ cd -

/var/log/apache2:$ cd -

/etc/apache2/conf.d:$ cd -

/var/log/apache2:$

ログと設定ファイルのディレクトリを 行き来するときなど便利です。

chown コマンドでグループも変更する

Unix/Linux には ファイルの所有権を変更する chown というコマンドがあります。 このコマンドは ユーザだけでなくグループも同時に変更することができます。

所有権のユーザだけ変える書式は次のようになります。

# chown testuser1 /home/testuser1/test.txt

グループも同時に変える場合は コロン ":" で区切って グループ名を指定します。

# chown testuser1:testgroup1 /home/testuser1/test.txt

GNU 版では、コロンの代わりに ドット "." も使えますが プロキシの認証などユーザの区切りに コロンはよく使用するので コロンで覚えておく方が無難だと思います。

ユーザ名とコロンだけで グループ名を指定しない場合は 指定したユーザ名(下の場合は testuser1 )の ログイングループを指定したことになります。

# chown testuser1: /home/testuser1/test.txt

ログイングループを調べなくても良いので 覚えておくと便利です。

上の例とは逆にユーザ名を指定しない場合は グループの変更になります。 これは chgrp と同じです。

# chown :testgroup1 /home/testuser1/test.txt

オマケですが、cp などのコマンドと同様に -R オプションで再帰(サブディレクトリ以下にも適用)ができます。

# chown -R testuser1:testgroup1 /home/testuser1

パスワードを一括で変更する

Linux には パスワードを一括で変更できる chpasswd というコマンドがあります。 使い方は簡単で標準入力からユーザ名とパスワードのセットを渡すだけです。

例えば testuser1 のパスワードを hogehoge にする場合 次のように ユーザ名とパスワードをコロンで区切ってパイプします。

# echo "testuser1:hogehoge" | chpasswd

複数ユーザのパスワードを一括で変更する場合 次のように ファイルにユーザ名とパスワードのセットを記述します。

# vi passwd.txt
testuser1:hogehoge1
testuser2:hogehoge2
testuser3:hogehoge3

これをパイプで渡します。

# cat passwd.txt | chpasswd

全ユーザのパスワードを一括で初期化する場合など便利です。

Bash で 配列を使ってみる その 2

前回( Bash で 配列を使ってみる その 1 )は値の格納でしたが 今回は配列らしく for ループを使って値を出力します。

まずは 普通の? for 文です。 配列の値を全て出力するには次のように 添え字に "@" か "*" を使います。

$ ARRAY=(one two three four)

$ echo ${ARRAY[@]}
one two three four

$ echo ${ARRAY[*]}
one two three four

これを for 文で使用します。

$ ARRAY=(one two three four)

$ for item in ${ARRAY[@]}
> do
>     echo $item
> done
one
two
three
four

ただし、これは 1 つ問題があります。

Bash の for 文 その 2 で書きましたが for 文は IFS (デフォルトはスペースなど) で区切られた文字列でループさせることができるので 配列の要素が IFS を含む文字列の場合、次のようにループが増えてしまいます。

$ ARRAY=("one1 one2 one3" two three four)

$ echo ${ARRAY[0]}
one1 one2 one3

$ for item in ${ARRAY[@]}
> do
>     echo $item
> done
one1
one2
one3
two
three
four

これを防ぐためにプログラム言語のような for 文を使用します。

配列の添え字の最大を取得するには次のようにします。

$ ARRAY=("one1 one2 one3" two three four)

$ echo ${#ARRAY[@]}
4

$ echo ${#ARRAY[*]}
4

これを for 文で使用します。

$ ARRAY=("one1 one2 one3" two three four)

$ for (( i = 0; i < ${#ARRAY[@]}; i++ ))
> do
>     echo ${ARRAY[$i]}
> done
one1 one2 one3
two
three
four

1 つめの配列も正しく出力されました。

Bash で 配列を使ってみる その 1

まずは値の格納です。 直接 1 つずつ指定する方法です。

$ A[0]=one
$ A[1]=two

もう一つは一括で指定する方法です。

$ A=(one two three four)

配列の値を出力するには "{}" (ブランケット)を 使用する必要があります。

$ echo ${A[0]}

ブランケットを指定しないと 変数 "$A" と "[0]" という文字の出力になってしまいます。

ちなみに 次のように添え字 0 の値は 添え字を指定しない変数の値と同じになります。

$ echo ${A[0]}
one

$ echo $A
one

ですのでブランケットを使用しない場合は次のように出力されます。

$ echo $A[0]
one[0]

IFS (Internal Field Separator) の値を表示する

Bash の for 文 その 2 で IFS を出したんですが、 デフォルトの IFS の中身を表示することができないかなと 考えていました。

普通に出力しても次のようになってしまいます。

$ echo "[$IFS]"

[
]

次のサイトに良い方法が載っていました。

【参考サイト】
IFS - http://www.curri.miyakyo-u.ac.jp/pub/doc/sh/node31.html

$ echo -n "$IFS" | od -b

0000000 040 011 012
0000003

$ echo -n "$IFS" | od -c

0000000      \t  \n
0000003

040 011 012 は 8進数です。
値としては スペース(0x20)、水平タブ(0x09)、改行(0x0A)になります。

Bash の for 文 その 2

Bash の for 文 の続きです。

文字列内のリスト

次のように文字列の空白で区切られた項目をリストとして ループさせることができます。

$ ITEMS="one two three four"

$ for item in $ITEMS
> do
>     echo $item;
> done;

one
two
three
four

正しくは空白ではなく IFS (Internal Field Separator) で定義されているもので 文字列を分割します。 IFS を変更すると区切りを変えることができます。

$ ITEMS="one/two/three four"
$ IFS="/"

$ for item in $ITEMS
> do
>     echo $item;
> done;

one
two
three four

引数のリスト

他に引数のリストでループさせるというのもあります。

$ function test () {
>     for item in $*
>     do
>         echo $item;
>     done;
> }

$ test one two three four

one
two
three
four

Bash の for 文

Bash で使える for 文には色々な形があります。

候補のリスト

$ for item in one two three four
> do
>     echo $item
> done

one
two
three
four

in の後に並べた値をループさせます。

ディレクトリ・ファイルのリスト

$ for item in /tmp/*
> do
>     echo $item
> done

/tmp/aaaa.txt
/tmp/bbbb.txt

マッチする ディレクトリやファイルのパスでループさせます。

コマンドの結果のリスト

$ for item in $(wc /tmp/a.txt)
> do
>     echo $item
> done

0
3
9

コマンド置換を使って、実行結果でループさせます。

これを利用して seq コマンドを使う方法があります。

$ for item in $(seq 1 3)
> do
>     echo $item
> done

1
2
3

Java などの言語によくある for 文

言語でよくみかける 初期条件や繰り返し条件が セミコロンで区切られてる形式も使えます。

$ for (( item = 0; item < 3; item++ ))
> do
>     echo $item
> done

0
1
2

二重カッコ (()) を使います。

Bash で プロンプトの色を変える

SSH などでサーバにログインして さらにそこから別のサーバにログインしたりすると 自分がどのサーバを触っているかわからなくなるときがあります。

プロンプトにホスト名を表示するなどで 分かるようにしても 背景と化してしまうので “本番環境だけは!”というときに プロンプトの色を変えるというのはどうでしょうか。

$ PS1="\[\033[0;31m\][\u@\h:\w]$\[\033[0m\] "
$ PS2="\[\033[0;31m\]>\[\033[0m\] "

こんな感じで設定します。 PS1 は通常のプロンプト、PS2は、入力が続いている場合のプロンプトです。

設定すると次のようになります。

[user1@host1:~]$
[user1@host1:~]$ cd \
> /usr/local/share

[user1@host1:/usr/local/share]$ 

突然目に飛び込んでくる赤色にドキッとさせられます。

Bash の コマンド置換

Bash のコマンド置換には、"`" (バッククォート)を使う形式と "$()" を使う形式の 2 種類があります。

$ echo `pwd`
/usr/local

$ echo $(pwd)
/usr/local

"$()" の形式の方が新しいようです。

この 2 つは、基本同じ動きをしますが、バックスラッシュによるエスケープの処理が違います。 次のようになります。

$ AA=abc

$ echo `echo $AA`
abc

$ echo `echo \$AA`
abc

$ echo $(echo $AA)
abc

$ echo $(echo \$AA)
$AA

Bash の文字列の文字数を取得する

次のような変数があります。

$ K="abc def gh"

wc コマンドで文字数を取得できますが echo するだけでは行末に改行が入ってしまうので "-n" オプションを付ける必要があります。

$ echo -n $K | wc -m

10

その他に "#" を付ける方方もあります。

$ echo ${#K}

10

"#" は、変数が配列の場合は要素数が取得できますが 変数が文字列の場合、文字数を取得することもできます。

$ K="あいう"
$ echo ${#K}

3

Linux で行数を取得する

Unix/Linux には wc というコマンドがあり 行数やバイト数、単語数を取得することができます。

$ wc -l test.txt
558 test.txt

"-l" のオプションで 行数を取得できます。

ファイルだけでなく、コマンドの結果を パイプで渡すこともできます。

$ cat test.txt | sort | wc -l
558

grep した結果が何件あるか、などにも使うことができます。

$ RESULT=`cat test.txt | grep keyword | wc -l`
$ echo $RESULT
32

sudo: sorry, you must have a tty to run sudo

sudo を cron などからバッチ処理で使用しようとすると 次のようなエラーが発生することがあります。

sudo: sorry, you must have a tty to run sudo

「端末から実行しないとダメ」と言ってるのですが どうしても cron で実行したい場合は、 sudo の設定ファイル /etc/sudoers の Defaults requiretty パラメータを修正します。

/etc/sudoers は visudo で編集します。

# visudo
【変更前】
Defaults requiretty

【変更前】
Defaults !requiretty

特定のユーザのみ許可したい場合は次のように設定します。

Defaults:hogehoge !requiretty

これで、ユーザ hogehoge は 端末なしで sudo できます。

オープンソースカンファレンス2010 Kansai@Kobe 3/13

オープンソースカンファレンス (OSC) が 3/13 (土) に 神戸で開催されることになりました。

オープンソースカンファレンス2010 Kansai@Kobe - Beef Up Kobe!

関西は 京都で開催されてきましたが 神戸も追加されたようです。

以下は、事務局からの案内メールの抜粋です。

□■□ --【オープンソースカンファレンス2010 Kansai@Kobe】-- □■□

★セミナー参加登録受付中!⇒⇒⇒ http://www.ospn.jp/osc2010-kobe/

◆日時:3月13日(土)10:00-18:00
◆入場:無料
◆会場:神戸市産業振興センター (兵庫県神戸市)
◆主催:オープンソースカンファレンス実行委員会
◆共催:地域ICT推進協議会(COPLI)
◆内容:オープンソース関連の最新情報提供 (展示・セミナー)

もちろん今年も 7月に Kansai@Kyoto も開催されます!
7/9 (金) 7/10 (土) @京都コンピュータ学院 です。

プロンプトに実行結果を表示する

シェル (bash) のプロンプト改造のネタです。

プロンプトは 環境変数 PS1 を変更することで 表示内容を自由に変えることができるのですが 今回は コマンドなどの実行結果 ($?) を表示させてみます。

$ PS1="\u:\w[code:\$?]$ "

hogehoge:~[code:0]$

これで OK です。

次のように エラーが発生すると エラーコードが表示されます。

hogehoge:~[code:0]$ ls aaaaaaa
ls: aaaaaaa: そのようなファイルやディレクトリはありません

hogehoge:~[code:2] $

.bashrc などでわざわざ設定する必要はありませんが シェルを作成するときに 一時的に設定すれば 結果が確認しやすくなります。

シェルで文字列のマッチング

シェルで文字列のマッチングするには、シェルの構文だけではできないので 次のように test をうまく使います。

$ if [ ! -z `echo $文字列 | grep "パターン"` ]; then

"-z" は、文字列の長さが 0 なら真になるオプションです。 バッククォーテーション内で echo した文字列が パターンにマッチすると その文字列を返すので それを比較に利用しています。

$ FILE=test_file_0209.csv

$ if [ ! -z `echo $FILE | grep "^test_file_....\.csv$"` ]; then
>      echo "match"
> else
>     echo "no match"
> fi

match

といった感じになります。

perl の正規表現を使いたい場合は "-P" オプションを付けます。

$ if [ ! -z `echo $文字列 | grep -P "パターン"` ]; then

sudo で 環境変数を引き継ぐ

sudo を使うと root の権限でコマンドを実行することができますが デフォルトの設定では、環境変数も変わってしまうため 権限だけ root にすることができません。

というわけで、環境変数を引き継ぐようにしてみます。

sudo の設定ファイル /etc/sudoers を visudo で編集します。

# visudo

次のような箇所があります。

Defaults    env_reset
Defaults    env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KD
                        LS_COLORS MAIL PS1 PS2 QTDIR USERNAME \
                        LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDEN
                        LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_N
                        LC_PAPER LC_TELEPHONE LC_TIME LC_ALL LANGUA
                        _XKB_CHARSET XAUTHORITY" 

これは引き継ぐ環境変数を設定しています。

特定のユーザ (testuser1) で、全ての環境変数を引き継ぐには 次のような行を追加します。

Defaults:testuser1 !env_reset

testuser1 で sudo を使うと 環境変数を引き継いでくれます。

sudo で パスワードを聞かれないようにする

sudo コマンドを使用するときに パスワードを聞かれないようにする設定のメモです。

sudo の設定ファイルは /etc/sudoers ですが 編集するときは、直接開くのではなく visudo というコマンドを使用します。

ALL     ALL=(ALL)       NOPASSWD: ALL

左から、ユーザ、ホスト、権限、コマンドなのですが、 コマンドに NOPASSWD: と付けることでパスワードが聞かれなくなります。

特定のユーザに対して 特定のコマンドのみを許可したい場合は 次のように記述します。

user1   ALL=(ALL)       NOPASSWD: /etc/init.d/httpd

これで user1 は httpd の実行ができるようになります。

複数ファイルの一括置換

Linux 複数のテキストファイルの文字列を一括で 置換するためのシェルスクリプトを作ってみました。

#!/bin/bash
# ==========================================================
#  複数ファイル一括置換コマンド
#  -----------------------------------------------------
#  create : 2010/01/21 Studio ODIN
#  update :
# ==========================================================

# --- init ---------------------------------------------
COMMAND_NAME=bulk_replace
CUR_DIR=`pwd`

# -- args ----------------------------------------------
TARGET_WORD=$1
REPLACE_WORD=$2
TARGET_DIR=$3

# -- args check ----------------------------------------
if [ "$TARGET_WORD" = "" ]; then
    echo "target word is nothing." >&2
    echo "Usage: $0 targetword replaceword [targetpath]" >&2
    exit 1
fi
if [ "$REPLACE_WORD" = "" ]; then
    echo "replace word is nothing." >&2
    echo "Usage: $0 targetword replaceword [targetpath]" >&2
    exit 1
fi
if [ "$TARGET_DIR" = "" ]; then
    TARGET_DIR=.
fi

# -- execute -------------------------------------------
grep -R -l "${TARGET_WORD}" "${TARGET_DIR}" | while read file
do
    tfile=`mktemp -u`
    cp -f -p $file $tfile > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        sed -e "s/${TARGET_WORD}/${REPLACE_WORD}/g" $file > $tfile
        if [ $? -ne 0 ]; then
            echo "Warning: failed write '$file'"
        fi
        mv -f $tfile $file > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo "Warning: failed rewrite '$file'"
            rm -f $tfile > /dev/null 2>&1
        fi
    else
        echo "Warning: failed copy '$file'"
    fi
done

# -- normal end ----------------------------------------
cd $CUR_DIR
exit 0

# ==========================================================
#  shell end
#  location: /usr/local/bin/bulk_replace
# ==========================================================

実行する場合は、パスの指定など大事なファイルが 大変なことにならないように気を付けてください。 (自己責任でお願いします)

こんなコマンドは、すでにあるような気もしますが 自分で処理を考えて作ってみるのは面白いと思います。

相対パスから絶対パスを取得する

Linux で realpath などが入っていない状況で 相対パスから絶対パスを取得するための シェルスクリプトです。

#!/bin/bash
# ==========================================================
#  絶対パス取得コマンド
#  -----------------------------------------------------
#  create : 2010/01/07 Studio ODIN
#  update :
# ==========================================================

# --- init ---------------------------------------------
COMMAND_NAME=fullpath
CUR_DIR=`pwd`

# -- args ----------------------------------------------
TARGET_OBJECT=$1

# -- args check ----------------------------------------
if [ "$TARGET_OBJECT" = "" ]; then
    pwd
    exit 0
fi
if [ ! -e "$TARGET_OBJECT" ]; then
    echo "Error: '$TARGET_OBJECT' is not exists." >&2
    exit 1
fi

# -- get full path (is directory) ----------------------
if [ -d $TARGET_OBJECT ]; then
    cd $TARGET_OBJECT
    pwd
    exit 0
fi

# -- get full path (is not directory) ------------------
cd `dirname $TARGET_OBJECT`
TARGET_PATH=`pwd`
TARGET_NAME=`basename $TARGET_OBJECT`
if [ "$TARGET_PATH" = "/" ]; then
    echo $TARGET_PATH$TARGET_NAME
else
    echo $TARGET_PATH/$TARGET_NAME
fi

# -- normal end ----------------------------------------
cd $CUR_DIR
exit 0

# ==========================================================
#  shell end
#  location: /usr/local/bin/fullpath
# ==========================================================

次の例は、fullpath という名前で /usr/local/bin に作成して実行権限を付けています。 環境に合わせて変更してください。

# cd /usr/local/bin
# vi fullpath
# chmod 755 fullpath

引数を指定しなければ、カレントディレクトリのパス、 ディレクトリやファイルを指定すれば、その絶対パスを返します。

$ fullpath
/home/odin

$ fullpath ../
/home

$ fullpath /
/

$ fullpath ../../vmlinuz
/vmlinuz

Debian で Go 言語を試してみる

Google が 発表した Go 言語を とりあえず試してみます。

The Go Programming Language

↓を参考にしてインストールしていきます。

Installing Go

早速 Go 言語を試してみる! (IT戦記)

ちなみに環境は Debian Linux です。

コンパイラなどが入ってない場合は、インストールします。

# apt-get install bison gcc libc6-dev ed make

Mercurial をインストールします。

# apt-get install python-setuptools python-dev
# easy_install mercurial

Go 言語で開発するユーザの環境変数を設定します。
.bashrc などに書いておきます。

export GOOS=linux
export GOARCH=386
export GOROOT=$HOME/go
export GOBIN=$HOME/bin
export PATH=$GOBIN:$PATH 

GOOS は、Linux なら "linux"、Mac OS X なら "darwin" を設定します。 GOARCH は、32-bit x86 なら "386" を設定します。

開発用のディレクトリを作成します。

$ mkdir ~/go
$ mkdir ~/bin 
$ mkdir -p ~/dev/go

ソースコードを取得します。

$ hg clone -r release https://go.googlecode.com/hg/ $GOROOT

ビルドします。

$ cd $GOROOT/src
$ ./make.bash

"hello world" を 出力するプログラムを作成します。
ソースファイルを hello.go とします。

$ cd ~/dev/go
$ vi hello.go
package main

import "fmt"

func main() {
    fmt.Printf("hello world\n")
}

386 の場合、8g コマンドでコンパイルします。 amd64 は 6g、armなら 5g です。

$ 8g hello.go

できた hello.8 というファイルを 8l コマンドで リンクします。

$ 8l hello.8

8.out という実行ファイルができるので実行します。

$ ./8.out
hello world

これで終わりです。

$GOBIN には、8g 8l の他にも 8c 8a などのコマンドが作成されています。 8c は C のコンパイル用のようですが 8a はアセンブラでしょうか? (未確認)

sl コマンド

■UNIXを学びながら笑ったものいろいろ (狐の王国)

この記事を読んで思い出したのですが sl というコマンドがあります。 実行すると 次のような SL が画面を走りぬけけます。

      ====        ________                ___________
  _D _|  |_______/        \__I_I_____===__|_________|
   |(_)---  |   H\________/ |   |        =|___ ___|  
   /     |  |   H  |  |     |   |         ||_| |_||  
  |      |  |   H  |__--------------------| [___] |  
  | ________|___H__/__|_____/[][]~\_______|       |  
  |/ |   |-----------I_____I [][] []  D   |=======|__
__/ =| o |=-~~\  /~~\  /~~\  /~~\ ____Y___________|__
 |/-=|___||    ||    ||    ||    |_____/~\___/       
  \_/      \__/  \__/  \__/  \__/      \_/           

ガチャガチャと ディレクトリを移動しながら ファイルを探しているときなど ls コマンドを間違えて sl と打ってしまうことがあります。 (少なくとも私は、よくあります・・・) このコマンドが入っていると、SL が走りぬけるまで ボーっと画面を見ることになります。

ジョークコマンドのようですが、存在しないコマンドを 打つと PATH 中を全て探すため 昔のコンピュータでは 時間がかかり、ダミーの sl コマンドを入れていた、 というのが起源のようです。 (ただし、このコマンドの場合は 逆に時間がかかります)

ちなみに、Debian では、パッケージが用意されています。

# apt-get install sl

-a, -l -F のオプションで変化します。

最近は JavaScript版 まであるようです。

Linux の コマンドラインで添付ファイル付きメール送信

mutt というコマンドで 簡単に添付ファイル付きの メールを送信することができます。

yum の場合、次のようにインストールできます。

# yum install mutt

次のコマンドで、本文に「message」、件名に「subject」、 添付ファイル「test.txt」を付けて 「xxx@example.com」にメールを送信します。

$ echo "message" | mutt -a test.txt -s "subject" xxx@example.com

サーバからログファイルを送る場合など便利です。

Linux で CD から ISO イメージを作る

Linux で CD から ISO イメージを作るには dd コマンドを使うだけなので簡単です。

# dd if=/dev/cdrom of=./image.iso

たったこれだけです。

作った ISO イメージを マウントすることもできます。

# mount -o loop ./image.iso /mnt/cdrom

たったこれだけです。

1 日以上前に更新されたファイルを検索する

ちょっとハマったのでメモです。

Linux の find コマンドの mtime オプションで ファイルの更新日を指定して検索することができます。 さらに "+/-" で、それより前/後を指定することもできます。

$ find -mtime +1

つまりこれで 1 日以上前 ( 24 時間より前 ) ということに なるような気がするのですが 実際にはそうなりません。

これに関してですが、次のように 1 だけを指定すると 更新してから「 24 時間〜 47 時間 59 分」のファイルが 対象になるわけです。

$ find -mtime 1

そして、24 時間以内の更新ファイルを対象とする場合 次のように 0 を指定します。

$ find -mtime 0

つまり +1 は「 47 時間 59 分」より前ということなので 実質的には 2 日前ということになります。 そして 1 日以上前のファイルを検索するには +1 ではなく +0 を指定しなくてはなりません。

$ find -mtime +0

Linux の date コマンドで前日の日付を取得する

UNIX/Linux の date コマンドは、書式を指定して 日付を取得できるので、ファイル名などに日付を付けたいときに とても便利です。

この date コマンドは、日付を指定して取得することもできます。

"--date" オプションを使って 1 日前なら次のように指定します。

$ date +"%Y/%m/%d" --date "1 day ago"

3 日後なら次のように指定します。

$ date +"%Y/%m/%d" --date "3 days"

"day" でも "days" でも取得できます。

バッチ処理で、前日の日付が必要なときなど 役に立ちます。

余談ですが、日付指定のオプションは "-v" だと思っていたのですが どうやらそれは BSD 系の UNIX の date コマンドのようで Redhat や Debian は "--date" になっていました。

yum で プロキシを利用する

yum コマンドで プロキシを利用する場合のメモです。

サーバ全体で設定するには yum の設定ファイルに追記します。

# vi /etc/yum.conf
proxy=http://192.168.1.200:3128/
proxy_username=USER
proxy_password=PASS

一時的に設定するには 次のように環境変数を設定します。

$ export http_proxy="http://USER:PASS@192.168.1.200:3128/"

sshd の blacklisted エラー

新しく Debian のサーバを入れたときに 別のサーバから既存の鍵を 移行しようとしてハマりました。

authorized_keys コピーして 既存の秘密鍵で ログインしようとしたのですが 次のようなエラーが出てログインできませんでした。

auth.log:488:Feb 21 06:31:01 XXXX sshd[1296]: error: Host key ... 
blacklisted (see ssh-vulnkey(1))

調べてみると どうやら一部の Debian、Ubuntu などで 生成されたOpenSSLベースの鍵は、 プロセス ID を元に乱数としており 乱数の使用方法に問題があったため 鍵の作り直しが必要になるようです。

これに該当するようで エラーが出ていたようです。

鍵を作り直さなければならないのですが とりあえず 一時的に接続できるようにするには sshd_config に 次の設定を追加します。

PermitBlacklistedKeys yes

とりあえずは これで繋がるようになりました。

問題ある鍵(脆弱な鍵)を調べるための コマンドも用意されていました。 次のコマンドで、全ての(標準の場所に格納されている)鍵を チェックします。

# ssh-vulnkey -a

Not blacklisted: 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx root@xxx
Not blacklisted: 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx root@xxx
COMPROMISED: 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx user@xxx

COMPROMISED と出ている鍵が NG (脆弱) です。

squid のログの時刻につて

squid の ログは 先頭が時刻なのですが 次のように unix の時刻で表現されています。

1248217969.955     11 192.168.1.10 TCP_MISS/200 1939 GET http://www

このままでは使いにくいので perl で簡単に変換します。

#!/usr/bin/perl

while (<>) {
  chomp;
  my ($t,$d) = /^([\d\.]+)(.*)$/;
  my @lt = localtime($t); $lt[5]+=1900; $lt[4]++;
  printf("%04d\/%02d\/%02d %02d:%02d:%02d%s\n",@lt[5,4,3,2,1,0],$d);
}

これを パイプするか ログファイルを引数にして実行します。

$ cat access.log | ./convert.pl
1248217969.955     11 192.168.1.10 TCP_MISS/200 1939 GET http://www
1248217970.134    339 192.168.1.21 TCP_MISS/200 21245 GET http://cl
1248218095.936      0 192.168.1.33 TCP_MISS/200 32 GET http://www.g

次のような結果になります。

2009/07/22 08:12:49     11 192.168.1.10 TCP_MISS/200 1939 GET http:
2009/07/22 08:12:50    339 192.168.1.21 TCP_MISS/200 21245 GET http
2009/07/22 08:14:55      5 192.168.1.33 TCP_MISS/200 32 GET http://

オープンソースカンファレンス 2009 Kansai 2 日目

今日も行ってきました、OSC 2009 Kansai !

今日、受講したのは次のセミナです。

  • Firefox 出張ワークショップ(基礎編1)
  • Firefox 出張ワークショップ(基礎編2)
  • LinkStation/玄箱をハックしよう
  • 手作りAndroidケータイを作ってみよう
  • Samba活用テクニック&最新動向

Firefox のワークショップは、実機持込だったのですが、 ノート PC を持っていくのが面倒だったので 基礎編だけを聴講しました。 聞いただけですが、とりあえず自分用の簡単な小物なら作れそうです。

玄箱は、有名な 山下さん でした。 回路図ネタや、ジャンパーやらは、よくわかりませんでしたが、楽しそうにイキイキと話をされていたので、 聞いていて面白かったです。超満員でした。

2 日間ウロウロしていましたが、2 ヶ月分くらいの モチベーションを高められたと思います。

オープンソースカンファレンス 2009 Kansai 1 日目

リフレッシュも兼ねて OSC ( オープンソースカンファレンス ) 2009 Kansai の 1 日目に行ってきました!

戦利品です。

WRITE_0075_01

OSC では、色々なセミナを開催しているので 時間ごとに、受けたいものを選択します。 今回は次のセミナを受けてきました。

  • Linux技術者認定資格(LPIC)レベル2 技術解説セミナー
  • 世界中で使われている『Drupal』。どんなサイトでも作れてしまう変幻自在な凄さをお伝えします
  • 【特別講義】オープンソース、その意義と可能性

LPIC の レベル 2 は取得済みですが、 毎回 びぎねっと の宮原さんのセミナを受けているので 今回も受講しました。 (びぎねっとは今回 OSC の運営もしていました。お疲れ様でです)

関西は関東に比べられない程、セミナなどが少ないので、こういうチャンスは逃せません。 好きなことをやってる人が好きな人同士で集まるので、いるだけでモチベーションも上がってきます。 会社の先輩も来ていたので、終わった後、熱く語り合ってしまいました。

送信専用の sendmail の設定

外部にメールサーバがあって sendmail を 送信のコマンドとしてのみ使用する場合の設定です。 LAN 内のシステムを作成していて 別にメールサーバがある場合がよくあります。

自サーバでは、メールサーバを動かす必要がないため デーモンとして sendmail を動かす必要はありません。

sendmail.cf は サーバ用の設定ファイルなので mail コマンドが使用する submit.cf を設定します。

まず submit.mc を編集します。

# vi /etc/mail/submit.mc

次の設定を変更します。

FEATURE(`msp', `[127.0.0.1]')dnl

外部のメールサーバが mail.example.com のとき 次のように記述します。 "dnl" は、コメントアウトです。

dnl FEATURE(`msp', `[127.0.0.1]')dnl
FEATURE(`msp', `mail.example.com')dnl

ついでに 次のように記述して ホスト名を設定できます。

送信メールが xxxx@example.com の様になります。

define(`confDOMAIN_NAME', `example.com')dnl

編集が終わったら、次のコマンドで submit.mc から submit.cf を作成します。

# m4 /etc/mail/submit.mc > /etc/mail/submit.cf

Redhat では sendmail-cf パッケージが入っていないと m4 コマンドがないので インストールする必要があります。

# yum install sendmail-cf

sendmail の 送信失敗したメールのキューを再送する

sendmail を使っていて送信に失敗したメールは キューとして溜まっていきます。

sendmail.cf に問題があって、 sendmail サーバからの送信に失敗している場合 次のディレクトリにファイルとして溜まっていきます。

/var/spool/mqueue

不要であればファイルを削除しても良いのですが 再送する場合は、次のコマンドを使用します。

# sendmail -q

submit.cf に問題があって、 SMTP サーバへの送信に失敗している場合 次のディレクトリにファイルとして溜まっていきます。

/var/spool/clientmqueue

これを再送するには 次のコマンドを使用します。

# sendmail -q -Ac

wget で プロキシを利用する

wget は、コマンドラインで HTTP や FTP の データを取得するコマンドです。 wget で プロキシを利用する場合の設定方法を メモとして書いておきます。

プロキシサーバのIPアドレスが 192.168.1.200、 ポート番号が 3128 の場合、 次のように環境変数 http_porxy を設定します。

$ export http_porxy=192.168.1.200:3128
$ wget http://www.yahoo.co.jp/

プロキシにユーザ認証が必要な場合、 次のように wget のオプションを指定します。

$ export http_porxy=192.168.1.200:3128
$ wget --proxy-user=username \
       --proxy-password=passwrod \
       http://www.yahoo.co.jp/

"\" (円マーク/バックスラッシュ)は、コマンドの途中で改行するための記号です。

コマンドの条件付き連続実行

コマンドは ";" (セミコロン) で並べて書くことができます。

$ echo 1; echo 2; echo 3
1
2
3

これは "|" (パイプ) のように出力を次のコマンドに渡したりは せず、単純に連続で実行します。

";" (セミコロン) ではなく "&&" (AND演算子) を使うと 前のコマンドが正常終了した場合のみ 次のコマンドを実行します。

$ echo 1 && false && echo 2
1

"false" は、必ず異常終了するコマンドです。 "false" で異常終了したため、 "echo 2" が実行されませんでした。

逆に前のコマンドが異常終了した場合のみ 次のコマンドを実行する "||" (OR演算子) もあります。

$ false || echo 1
1

コマンドが異常終了したときに 特別な処理をさせるなどできます。

あくまで前のコマンドの実行結果がどうか ということなので、次のように書いた場合 先頭から順に判定されます。

$ echo 1 && false || echo 2 && false || echo 3
1
2
3

シェルで位置パラメータのセット

シェルで変数に値をセットする場合は 次のようにします。

$ var1=KAME

$ echo $var1
KAME

コマンドの実行結果をセットする場合は 次のようにします。

$ var2=`echo KAME to USAGI`

$ echo $var2
KAME to USAGI

この例では、$var2 に "KAME to USAGI" とセットされてしまいますが 別々の変数にセットしたい場合があります。

そんなときには、set コマンドを使用します。

set コマンドは、次の例のように 引数を $n の位置パラメータにセットすることができます。

$ set KANI SARU KAKI

$ echo $1
KANI
$ echo $2
SARU
$ echo $3
KAKI

コマンドの結果をセットするには 次のように書きます。

$ set `echo KAME to USAGI`

$ echo $1
KAME
$ echo $2
to
$ echo $3
USAGI

コマンドと定数を混ぜて書くこともできます。

$ set KURI `echo KANI KAKI` ONIGIRI

$ echo $1,$2,$3,$4
KURI,KANI,KAKI,ONIGIRI

値の中に set のオプションとして理解されそうなものが ある場合は、"--" オプションを指定します。 "--" オプション以降は全て引数と判定されます。

$ set -- -x

$ echo $1
-x

位置パラメータをクリアするには、 "--" オプションだけを指定します。

$ set --

nkf で文字コード変換

Linux では、nkf というコマンドを使って、文字コードの変換ができます。

次の例は Shiht_JIS の input_file を UTF-8 に変換して output_file に 出力しています。

$ nkf -S -w -x -O input_file output_file

"-S" は、読込ファイルの文字コードが Shift_JIS という指定です。 付けなくても自動判定しますが、読込ファイルの文字コードが わかっているときは、付けておいた方が誤認識しなくて良いです。

"-w" は、出力の文字コードが UTF-8 という指定です。

"-O" は、最後に 出力ファイルを指定するオプションです。 このオプションを指定しないと標準出力に出力します。 データが多くなると、標準出力をファイルにリダイレクトするより このオプションで出力ファイルを指定する方が速度が出ます。

次の表は、文字コードのオプションです。

文字コード読込出力
Shift_JIS-S-s
JIS-J-j
EUC-JP-E-e
UTF-8/UTF-8N-W-w

SELinux の状態変更

SELinux は、便利ですが、アクセスエラーなどの問題が発生したときに 処理が悪いのか SELinux が影響しているのか判断し難いときがあります。

そんなときは、SELinux を一時的に無効にして、 処理を確認するのが有効です。

まず、現在の状態を確認するには、次のコマンドを使います。

# getenforce

Enfocing や Permissive といった状態名が表示されます。 状態名の内容は次の表のようになります。

状態 機能 制御
Enfocing 有効 有効
Permissive 警告のみ 無効
Disabled 無効 無効

Enfocing は、SELinuxが 全て有効な状態です。

Permissive は、SELinuxが 警告を表示するだけで、制御はしない状態です。 原因が SELinux か判断できないときに、この状態に変更して確認します。

Disabled は、SELinuxが 全て無効な状態です。

次のコマンドで SELinux の状態を、 一時的に Permissive にすることができます。 再起動すると元に戻ります。

# setenforce 0

次のコマンドで SELinux の状態を、 一時的に Enfocing にすることができます。 こちらも再起動すると元に戻ります。 このコマンドで、状態を Disabled にすることはできません。

# setenforce 1

永続的に変更するには、次のファイルの SELINUX の値を変更して再起動します。

# vi /etc/sysconfig/selinux
SELINUX=disabled

ファイルを 0 バイトにする

追記型でログファイルなどを作成していると ファイルを 0 バイトでクリアしたいときがあります。

次ようなコマンドで ファイルを 0 バイトにします。 (ファイルが存在しなければ、 0 バイトのファイルを作成します)

$ cat /dev/null > 0.txt

DOS の場合は、次のようにコマンドを打ちます。 null ではなく、nul なので注意してください。

C:\> type nul > 0.txt

tar コマンドで 不要なファイルは除外する

tar コマンドを使って固めるときに パスの指定の方法によっては 不要なファイルやディレクトリが混ざる場合があります。

必要なファイルのみを指定するのが難しい場合、 不要なファイルを除外する方法があります。

まずは何も指定しない場合です。

$ tar cvf test.tgz  test
test/
test/a/
test/a/aaa1.txt
test/a/aaa2.txt
test/c/
test/c/ccc1.txt
test/b/
test/b/bbb1.txt

test/a の配下が不要な場合 次のように "--exclude" オプションを使って 不要なパスのパターンを指定します。

$ tar cvf test.tgz --exclude 'test/a*'  test
test/
test/b/
test/b/bbb1.txt
test/c/
test/c/ccc1.txt

test/a を除外することができました。

ただし、test/a.txt のようなファイルも除外してしまうので パターンを指定する場合は注意してください。

Linux のサーバ間ファイル転送

複数台の Linux サーバを使って作業していると サーバからサーバにファイルを転送したいときがあります。

ssh を動かしていれば、簡単にファイルを転送することができます。

次の例は、scp コマンドを使って、 ローカル(ログインしているサーバ)の test.txt を 192.168.1.101 の /tmp/test.txt に転送しています。

$ scp test.txt username@192.168.1.101:/tmp/test.txt

次の例のように ssh コマンドを使って ディレクトリを固める→送る→展開する を パイプを使って 行うこともできます。

$ tar cf - test | ssh username@192.168.1.101 'cd /tmp;tar xf -'

パイプを使って転送すると 一時ファイルが不要なだけでなく コマンドを使いこなしてる! という感じがします。

Red Hat で vsftpd

Red Hat Enterprise Linux 5 の導入していたときの話ですが、 vsftpd の ftp サーバを入れて 接続しようとしたところ エラーで怒られてしまいました。

ftp 用のアカウントを作成して そのホームディレクトリ以下しか アクセスできないようにしたのですが、 ホームディレクトリのアクセスに対して SELinux が怒っているようでした。

# setsebool -P ftp_home_dir 1

そんな場合、上記の呪文で ftp_home_dir の値を active にします。

ちなみに "-P"オプションを付けないと、再起動後に元に戻ってしまいます。

EcolinuxをUSBブートしてみる

Ubuntu8.10が起動するUBSメモリを 会社に持って行ったんですが、1台だけ起動ないPCがありました。 画面の解像度辺りの問題かな、とか思いつつも、 せっかくなので違うLinuxを入れて試してみることにしました。

Ecolinux
http://ja.ecolinuxos.com/

UBSメモリのUbuntu8.10が動かなかったPCでも、とりあえずこのLinuxは動きました。

「必要なアプリケーションは後からインストールする」という方針のため、軽量だそうです。 また、「できるだけ多様なハードウェアに対応する」ということと 「Ubuntuをベースとした堅牢なパッケージシステムと安定したリポジトリ」ということで USBメモリで持ち運ぶには丁度良さそうです。

ダウンロードしてきたISOファイルをVirtualBoxで起動させて、 VirtualBox上でUSBメモリに超簡単にインストールすることができました。

そのうちインストール手順を書くつもりです。

インストールしたUSBメモリをVirtualBox上で起動させることができれば スクリーンショットなんかも載せることができたのですが・・・。

これはこれで調べないと。