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コマンドが実行できない - takafumi blog
基本的に 対話モードじゃないとダメらしいです。
確かに 別のコマンドを alias されていたら危険ですね。
(使っている Linux は Ubuntu 14.04.3 LTS です)
watch コマンドは 指定したコマンドを一定間隔で実行して 出力結果の差分を表示してくれますが この「一定間隔でコマンドを実行する」という機能は それだけで便利です。
次のようにすると 3秒ごとに Webサーバにアクセスします。
$ watch -n 3 wget -q http://localhost/hogehoge -O - > /dev/null
繰り返すだけなら while などでもできますが 一定間隔で実行するなら watch コマンドを使うのが簡単で便利です。
(使っている Linux は Ubuntu 14.04.3 LTS です)
そんな状況があるかどうかわかりませんが Linux を使っていて ストップウオッチが必要になった場合の方法です。
$ time read
これだけです。
コマンドの実行で開始、 [Enter] を押すと時間が出ます。
$ time read
real 0m4.696s
user 0m0.000s
sys 0m0.000s
(使っている 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です。 とりあえず 空で定義しておいて 必要に応じて 後で再定義します。
(使っている 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
知っていれば読めますが 知らないと記号にしか見えません。
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 コマンドを使った小技です。
(使っている Linux は Ubuntu 14.04.3 LTS です)
script コマンドは 作業のログをとってくれる便利なコマンドですが そのコマンドを利用すると 別の端末の操作をモニタリングすることができます。
操作する側の 端末 A では、
script コマンドを
"-f" オプション付きで実行します。
$ script -f /tmp/test.log
-f, --flush run flush after each write
"-f" は 即時にログに出力するためのオプションで 接続が切れた場合にも安心のオプションです。
モニタリングする側の 端末 B では、
tail コマンドを
"-f" オプション付きで
実行し、
端末 A の
script コマンドのログを参照します。
$ tail -f /tmp/test.log
この状態で 端末 A で操作をすると 端末 B のコンソールにも同じ内容が出力されます。
ダブルチェックや 指導する場合などに便利だと思います。
※ 間違えて "script -f" を実行した 端末 A で script コマンドの ログファイルを "tail -f" で開いてしまうと 無限ループに入ってしまうので注意してください。
メモです。
(使っている 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}
メモです。
(使っている 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.
メモです。
(使っている 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.
このオプションは、引数のファイルのタイムスタンプよりタイムスタンプが新しいファイルを検出してくれます。
引数のファイルのタイムスタンプ“より大きい”ため、そのファイル自体は含まれません。 引数のファイルが対象の中にあっても、検出されないようになってるわけです。 よく考えられてますね。
メモです。
(使っている 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'
メモです。
(使っている 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 の処理は エラーが発生しても次のステップへと 進んで行きます。
[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 には プログラム言語のように 未定義の変数をチェックできる オプション "-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 には 処理の実行内容をトレースできる デバッグに便利な オプション "-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 には 処理を実行せずに 文法のチェックだけをしてくれるオプション "-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 で以下のようなループ処理を書くと パイプ以降が別プロセスになってしまうため その中で設定した変数を、ループ処理の外で参照することができません。
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が入っている
( "<" の後の空白に注意してください)
プログラム的な処理が必要なときに 助かります。
以前、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 コマンドの "-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 という名前だそうです。
[参考]
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
find の "-mtime" などの日付検索の オプションは便利ですが コマンドの実行した時点が 基準日時(そこから24時間以内など)になるため ややこしかったりすることがあります。
ケースにもよりますが、
日付検索の
便利なオプションに
"-daystart" があります。
使い方は簡単。付けるだけ。
$ find . -daystart -mtime -3
"-daystart" は "-mtime" などより先に書きます。
このオプションで、日付検索の基準が00時00分になるため 現在時刻ではなく、日付単位で考えることができるようになります。
小ネタ。
$ date -d "2017/3/4 1:15" +"%Y/%m/%d %H:%M"
2017/03/04 01:15
date コマンドに 日付文字列を与えて 出力の書式を指定すれば、数字の0詰めも簡単。
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
色々と知らないことが増えるなぁ、という話。
Bash シェルを実行するときに 環境変数の設定の有無を確認しているのですが 基本文字列なので、パッと見が分かり易い 次のような書き方をよくしていました。
if [ "${VAR_XXXXX}" = "" ]; then echo "環境変数が未設定"
値が必須だったので、良かったのですが 今回たまたま配列を使うことになり、 同じ書き方をすると未設定にマッチしてしまいました。
調べてみると最近は test コマンドに -v オプションがあり 変数の定義の有無をチェックできるとか。
if [ ! -v VAR_XXXXX ]; then echo "環境変数が未設定"
これでイケました。
まだ環境は選ぶようですが、見た目もわかりやすくて良いですね。
前回に続き、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 での話です。
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:
ちょっと気になったのでメモ。 環境は 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 は変わりませんでした。
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 -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
たいしたことではないのですが。
コマンドの結果にヘッダーを付けたかったんですが cat でできたっけ?どうしようかと考えて結果力技。
$ (echo "MByte Directory") && (du -sm /home /opt /tmp /var)
MByte Directory
1 /home
1 /opt
87 /tmp
2 /var
コマンドをカッコで囲んで && でつなげたらできました。
Linux でユーザを追加する方法はいくつかりますが usermod を使う場合は注意が必要です。
$ usermod -aG "グループ名" "ユーザ名"
このとき、"-G" だけだと現在の設定されているグループが消えてしまうので "-aG" のように "a" を付け忘れないようにします。
gpasswd というコマンドを使う方法もあります。
$ gpasswd -a "ユーザ名" "グループ名"
このコマンドは、グループを操作するコマンドなので グループ名が最後に来ることに注意します。 ユーザ名は、"-a" オプションの引き数になります。
vi で開きたいけど 上書きしてしまうと怖いな というときに 読み取り専用で vi を開く view というコマンドがあります。
開くときは vi と同じです。
$ view test.txt
これなら ちょっと安全に使えますね。
ちなみに 読み取り専用で開きますが :w! のように感嘆符付きだと上書きできます。
サーバのバッチ処理なんかを作っていると、 「昨日のタイムスタンプのファイル」が 欲しくなるときがあります。
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 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)
簡単に生成する数字を指定できるので 確かに便利ですね。
コマンドを実行するときに コマンドの前に 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
使うときは、ちょっとだけ注意です。
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 のどのバージョンで作成したかですが file コマンドで確認することができました。 (Ubuntu10.04環境)
$ file database.sqlite2 database.sqlite2: SQLite 2.x database $ file database.sqlite3 database.sqlite3: SQLite 3.x database
おひさしぶりです。
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`
色々とありますね。
テキスト処理は覚えておくと、開発中のちょっとしたことに使えて便利なので 遊びながら覚えていきたいものです。
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 - とすることで 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 の 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
なるほど。
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
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 コマンドで表示されるタイムスタンプ書式は 環境変数 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 /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 /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 オプションを付ける。
前回の続き 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" が取得する情報の識別子です。
このコマンドを実行して、文字列がダーっと帰ってきたら成功です。
今回はここまで。
短いですが、前回の続き。
前回、 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:*
今回はここまで。
ちょっと 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 ネタです。
今回は 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 には乱数を取得できる変数?が 用意されています。
$ 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 コマンド取得したパスは -exec オプションを使って コマンドを実行することができます。
$ find . -mtime 3 -exec rm {} \;
-exec を複数書くと 複数のコマンドを実行することができます。
$ find . -mtime 3 -exec echo {} \; -exec rm {} \;
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 コマンドは シェルの組み込みコマンドです。
とりあえず 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 /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" オプションを付けても 確認してきます。 この確認を止めさせたいときに エイリアスを削除する以外で方法はないのかと思って 調べてみるとちゃんとありました。
コマンドの前にバックスラッシュを付けると エイリアスを無効にできるそうです。
試してみます。
$ 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 も同様で、機能の有効・無効化について書いてありました。
11/10 大阪で開催された『LPIC レベル3 303 Security 技術解説無料セミナー』で tripwire が解説されていたので、 せっかくなので Raspberry Pi に入れてみます。
tripwire は、ファイル改竄などを検出するためのツールです。
ある時点のファイルの状態をデータベースに保存しておいて 現時点の状態と比較することができます。
tripwire の設定ファイルやデータベース自体が改ざんされないように 認証用のキーを作成する必要があるのですが 最近は、インストールの流れで作成することができます。
apt-get します。
$ sudo apt-get update $ sudo apt-get install tripwire
途中、サイトキーを作成するためのパスフレーズを求められます。
サイトキーは、設定ファイルを改ざんされないためのキーです。
ローカルキーを作成するためのパスフレーズを求められます。
ローカルキーは、データベースを改ざんされないために使用するキーです。
インストールが終了したら、データベースを初期化します。 これによって、「この時点」のファイルの情報をデータベースに保存します。
$ 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:
データベースの更新なのでやはりローカルキーのパスフレーズが求められます。
ユーザ名を変更するメモです。
といってもまずグループ名。 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 でパイプを使ってコマンドを実行すると 終了ステータスは、最後のコマンドの結果になります。
たとえば次のような場合
$ 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 には tmpwatch は入ってないし、でも再起動すると /tmp は空になるので 何か掃除しているのか?と思っていたんですが 下のサイトを見ると /etc/init/mounted-tmp.conf に設定があるようです。
[参考]
おまぬけ活動日誌(2010-07-21)
/etc/default/rcS の TMPTIME というパラメータで 削除されるまでの猶予期間を設定できるそうです。
社内で wget を使ってるときは外部に接続するために プロキシの設定をしてあるのですが、 社内のサーバに接続するときなど プロキシを経由してほしくないってことがあります。
$ wget --no-proxy http://.....
こんな感じでプロキシを使用しないオプションをつけます。
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 のディストリビューションがわからない場合 Ubuntu だと次のように /etc/lsb-release を見ればわかるんですが CentOS は?と聞かれたのでメモ。
$ cat /etc/lsb-release
[参考]
インストールした Linux ディストリビューション名とバージョンを確認するには - PRiMENON:DiARY
上の参考サイトによると とりあえず /etc/issue 、 あとは /etc の下に xxxx-release みたいなファイルがあるようです。
なので次のようにしておけばよいかと。
$ cat /etc/issue /etc/*-release
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 コマンドをプロキシ経由で 使用するときのメモです。
環境変数で設定してやっても使えませんでした。
設定ファイルを変更します。
$ 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 を入れるメモです。
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 コマンドは "-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 > ?
こんな感じになります。
打ったコマンドに自信がないときや 目でチェックしながら流したいときに便利です。
こんなテキストがあります。
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 コマンドは、コマンドの実行時にファイルを検索するのではなく 事前に作成したデータベースを使って検索するので、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 を受けてきました。
ギリギリでしたが、いつものように簡単な受験体験記を書きました。
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 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]
これで前の続きからダウンロードすることができます。
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 の 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 では次のようにチルダ (~) で カレントユーザのホームディレクトリに移動することができます。
$ cd ~
チルダに続けてユーザ名を指定すると そのユーザのホームディレクトリに展開されます。
$ cat ~hogehoge/.bashrc
他のユーザのホームディレクトリを調べなくても ホームディレクトリを指定することができます。
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 というのもありますが こちらはファイルタイプを表示します。
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 の 変数展開には結構種類があります。
よく使うのは次の「文字列の末尾から最短マッチさせて削除」で 「ファイルの拡張子」の削除です。
$ ls *.txt | while read FILE > do > echo ${FILE%.txt} > done
"${FILE%.txt}" の部分が変数展開です。 これで ls コマンドで .txt のファイルだけを取得して ファイル名から .txt を削除した部分を出力することができます。
変数展開には次のようなものがあります。
${変数:+文字列} | 変数が存在し空でなければその文字列、それ以外なら空 |
---|---|
${変数:-文字列} | 変数が存在し空でなければその値、それ以外なら文字列 |
${変数:=文字列} | 変数が存在し空でなければその値、それ以外なら変数に文字列を設定 |
${変数:?文字列} | 変数が存在し空でなければその値、それ以外なら文字列を出力して終了 |
${変数:位置} | 位置から末尾までの部分文字列 |
${変数:位置:長さ} | 位置から長さ分の部分文字列 |
${変数#パターン} | 変数の先頭がパターンマッチした場合、最短マッチ部分を削除 |
${変数##パターン} | 変数の先頭がパターンマッチした場合、最長マッチ部分を削除 |
${変数%パターン} | 変数の末尾がパターンマッチした場合、最短マッチ部分を削除 |
${変数%%パターン} | 変数の末尾がパターンマッチした場合、最長マッチ部分を削除 |
${変数/パターン/文字列} | 最初にパターンマッチした部分を文字列で置換 |
${変数//パターン/文字列} | パターンマッチしたすべての部分を文字列で置換 |
Bash だけでも色々なことができます。
sudo でリダイレクトする場合、次のように書くと、リダイレクト先のファイルの権限は sudo したユーザになります。
$ sudo echo aaa > test.txt ~~~~~~~~~~ ←この部分は sudo とは無関係
リダイレクトごと sudo した権限で実行したい場合は 次のように書きます。
$ sudo sh -c "echo aaa > test.txt"
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 というコマンドがあります。
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 コマンドの 使い道を探していたのですが、素晴らしいネタが書いてあるサイトがありました。
【参考サイト】
しげふみメモ : Linuxのrevコマンドで文字を逆に並べる
ソートや文字の切り出しは考えていたのですが banner コマンドを使ったネタは想定外でした。
banner は 次のような出力ができるコマンドです。
$ banner Linux
# ### # # # # # #
# # ## # # # # #
# # # # # # # # #
# # # # # # # #
# # # # # # # # #
# # # ## # # # #
####### ### # # ##### # #
これを tac コマンドに通すと上下が逆になります。
$ banner Linux | tac
####### ### # # ##### # #
# # # ## # # # #
# # # # # # # # #
# # # # # # # #
# # # # # # # # #
# # ## # # # # #
# ### # # # # # #
rec コマンドに通すと左右が逆になります。
$ banner Linux | rev
# # # # # # ### #
# # # # # ## # #
# # # # # # # # #
# # # # # # # #
# # # # # # # # #
# # # # ## # # #
# # ##### # # ### #######
2 つのコマンドに通すと 180 度回転させることができます。
$ banner Linux | rev | tac
# # ##### # # ### #######
# # # # ## # # #
# # # # # # # # #
# # # # # # # #
# # # # # # # # #
# # # # # ## # #
# # # # # # ### #
アイデア次第だと感心させられます。
以前書いた 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
ソートに使うくらいで、他に「これぞ!」という使い道は思いつきませんが いつか必要になるときのために、こういったコマンドが用意されているのは安心ですね。
Linux には tac というコマンドがあります。
資格の学校とは全く関係はありません。
「 cat コマンドの逆」ということで tac です。
$ cat test.txt
abc
123
efg
456
$ tac test.txt 456 efg 123 abc
このように cat とは 逆順の出力になります。
デフォルトでは、区切り文字が改行になっているので 行単位で逆になります。 他のコマンドで行を逆にするのは結構大変なので 覚えておくと役に立つときがあるんじゃないでしょうか。
他にも if 〜 fi、case 〜 esac といったように 「逆の文字列」で何かを表現することがありますが、 テキストならではの発想で面白と思います。
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
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 だけ抽出できました。
列を指定して抽出できるので抽出条件が他の列に混ざっていても 大丈夫という利点があります。
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 コマンドはソートする列を指定することができます。
次のテキストファイル( 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 というコマンドがあります。
このコマンド、かなり便利なのですが、 できることが多過ぎるので覚えるのは結構大変だったりします。 「基本的な使い方はわかるけど・・・」という人も多いのではないでしょうか。 自分へのメモの意味も含めて機能を紹介したいと思います。
次のテキストを処理します。( 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 で開くと間違って上書き保存してしまうのが怖い」と 言われたことがあります。 vi を終了する際に ":wq" と打ってしまうそうなのですが これでは確かに更新日時が変わってしまいます。
そこで view というコマンドがあります。
$ view a.txt
このコマンドはファイルを vi の読み取りモードの "-R" オプションを付けた状態で開いてくれます。
この状態であれば ":w" で保存しようとしても「読み取り専用です」とエラーになります。 ただし ":w!" のような強制保存はできてしまうので注意して下さい。
vi の操作で保存だけしたくない、というときに安心なコマンドです。
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
他にも色々なオプションがありますが 「どうしても使わないとならない」という場面以外では 他のコマンドと組み合わせて使う方が シンプルで見やすくなると思います。
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
使用した例は、ちょこちょこ出していたのですが メモ代わりに単独でネタにしておきます。
オプションを付けずに echo を使うと 行末に改行が付きます。
$ echo ABC; echo XYZ
ABC
XYZ
"-n" オプションを付けることで 改行なしにできます。
$ echo -n ABC; echo -n XYZ ABCXYZ
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
ものすごい小ネタですが 知っていると便利です。
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:$
ログと設定ファイルのディレクトリを 行き来するときなど便利です。
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 で 配列を使ってみる その 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 つめの配列も正しく出力されました。
まずは値の格納です。 直接 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]
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 文 の続きです。
次のように文字列の空白で区切られた項目をリストとして ループさせることができます。
$ 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 文には色々な形があります。
$ 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
言語でよくみかける 初期条件や繰り返し条件が セミコロンで区切られてる形式も使えます。
$ for (( item = 0; item < 3; item++ )) > do > echo $item > done 0 1 2
二重カッコ (()) を使います。
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 のコマンド置換には、"`" (バッククォート)を使う形式と "$()" を使う形式の 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
次のような変数があります。
$ K="abc def gh"
wc コマンドで文字数を取得できますが echo するだけでは行末に改行が入ってしまうので "-n" オプションを付ける必要があります。
$ echo -n $K | wc -m
10
その他に "#" を付ける方方もあります。
$ echo ${#K}
10
"#" は、変数が配列の場合は要素数が取得できますが 変数が文字列の場合、文字数を取得することもできます。
$ K="あいう" $ echo ${#K} 3
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 を 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 できます。
オープンソースカンファレンス (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 を使うと 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 の設定ファイルは /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
Google が 発表した Go 言語を とりあえず試してみます。
↓を参考にしてインストールしていきます。
ちなみに環境は 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 というコマンドがあります。 実行すると 次のような 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版 まであるようです。
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 イメージを作るには dd コマンドを使うだけなので簡単です。
# dd if=/dev/cdrom of=./image.iso
たったこれだけです。
作った ISO イメージを マウントすることもできます。
# mount -o loop ./image.iso /mnt/cdrom
たったこれだけです。
ちょっとハマったのでメモです。
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
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 の設定ファイルに追記します。
# 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/"
新しく 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 の ログは 先頭が時刻なのですが 次のように 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://
今日も行ってきました、OSC 2009 Kansai !
今日、受講したのは次のセミナです。
Firefox のワークショップは、実機持込だったのですが、 ノート PC を持っていくのが面倒だったので 基礎編だけを聴講しました。 聞いただけですが、とりあえず自分用の簡単な小物なら作れそうです。
玄箱は、有名な 山下さん でした。 回路図ネタや、ジャンパーやらは、よくわかりませんでしたが、楽しそうにイキイキと話をされていたので、 聞いていて面白かったです。超満員でした。
2 日間ウロウロしていましたが、2 ヶ月分くらいの モチベーションを高められたと思います。
リフレッシュも兼ねて OSC ( オープンソースカンファレンス ) 2009 Kansai の 1 日目に行ってきました!
戦利品です。
OSC では、色々なセミナを開催しているので 時間ごとに、受けたいものを選択します。 今回は次のセミナを受けてきました。
LPIC の レベル 2 は取得済みですが、 毎回 びぎねっと の宮原さんのセミナを受けているので 今回も受講しました。 (びぎねっとは今回 OSC の運営もしていました。お疲れ様でです)
関西は関東に比べられない程、セミナなどが少ないので、こういうチャンスは逃せません。 好きなことをやってる人が好きな人同士で集まるので、いるだけでモチベーションも上がってきます。 会社の先輩も来ていたので、終わった後、熱く語り合ってしまいました。
外部にメールサーバがあって 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.cf に問題があって、 sendmail サーバからの送信に失敗している場合 次のディレクトリにファイルとして溜まっていきます。
/var/spool/mqueue
不要であればファイルを削除しても良いのですが 再送する場合は、次のコマンドを使用します。
# sendmail -q
submit.cf に問題があって、 SMTP サーバへの送信に失敗している場合 次のディレクトリにファイルとして溜まっていきます。
/var/spool/clientmqueue
これを再送するには 次のコマンドを使用します。
# sendmail -q -Ac
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 --
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 を一時的に無効にして、 処理を確認するのが有効です。
まず、現在の状態を確認するには、次のコマンドを使います。
# 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 バイトのファイルを作成します)
$ cat /dev/null > 0.txt
DOS の場合は、次のようにコマンドを打ちます。 null ではなく、nul なので注意してください。
C:\> type nul > 0.txt
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 サーバを使って作業していると サーバからサーバにファイルを転送したいときがあります。
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 Enterprise Linux 5 の導入していたときの話ですが、 vsftpd の ftp サーバを入れて 接続しようとしたところ エラーで怒られてしまいました。
ftp 用のアカウントを作成して そのホームディレクトリ以下しか アクセスできないようにしたのですが、 ホームディレクトリのアクセスに対して SELinux が怒っているようでした。
# setsebool -P ftp_home_dir 1
そんな場合、上記の呪文で ftp_home_dir の値を active にします。
ちなみに "-P"オプションを付けないと、再起動後に元に戻ってしまいます。
Ubuntu8.10が起動するUBSメモリを 会社に持って行ったんですが、1台だけ起動ないPCがありました。 画面の解像度辺りの問題かな、とか思いつつも、 せっかくなので違うLinuxを入れて試してみることにしました。
UBSメモリのUbuntu8.10が動かなかったPCでも、とりあえずこのLinuxは動きました。
「必要なアプリケーションは後からインストールする」という方針のため、軽量だそうです。 また、「できるだけ多様なハードウェアに対応する」ということと 「Ubuntuをベースとした堅牢なパッケージシステムと安定したリポジトリ」ということで USBメモリで持ち運ぶには丁度良さそうです。
ダウンロードしてきたISOファイルをVirtualBoxで起動させて、 VirtualBox上でUSBメモリに超簡単にインストールすることができました。
そのうちインストール手順を書くつもりです。
インストールしたUSBメモリをVirtualBox上で起動させることができれば スクリーンショットなんかも載せることができたのですが・・・。
これはこれで調べないと。