シェルスクリプト総合 その14
■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。
2009/01/29(木) 06:54:48スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>1-6くらい)をご覧ください。
□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
Linuxユーザは/bin/shの正体がbashなので特に注意。
FreeBSDユーザは/bin/shの正体がashなので注意。
v7 shに一番近くて、現役のshは、OpenSolaris由来のheirloom sh。
http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/sh/
http://heirloom.sourceforge.net/sh.html
・csh/tcshのシェルスクリプトは推奨されません。
(理由は「csh-whynot」でググれ)
・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。
manや参考リンクを見ましょう。
aproposないしはman -kでそれらしい単語による簡単な検索もできます。
・シェルスクリプトのことをシェルってゆーな
・シェルで使えるワイルドカード等は正規表現ではありません。
正規表現の話題はスレ違い(正規表現スレへ)
□初心者へのアドバイス:
・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも
RubyやPerlの方が適した仕事には素直にそちらを使いましょう。
・知らないコマンドが出てきたらmanを引きましょう。
・思い通りに動かないときは、まずは sh -x でトレースしましょう。
前スレ落ちたみたいなのでリンク省略。
0671名無しさん@お腹いっぱい。
2009/04/28(火) 22:16:41v7sh
cdも外出し。
0672名無しさん@お腹いっぱい。
2009/04/28(火) 22:29:15誤判定されない方法を考えてみた。
for f in *;do case `(cd "$f") 2>&1` in *'Not a directory')echo "$f";;esac; done
0673名無しさん@お腹いっぱい。
2009/04/28(火) 22:56:37> もしかしてOpenSolarisにも入っているのだろうか・・・
入ってない。TODOリストに入っているが、
ライセンス関係で新たに作らないといけない。
CDE/Motifべったりだから誰もやらないだろう。
>>666
わざわざエミュレータで動かしてます。
「現存」してないです。とあなたは言うでしょうけど。
0674名無しさん@お腹いっぱい。
2009/04/29(水) 01:06:420675名無しさん@お腹いっぱい。
2009/04/29(水) 02:37:45美しくないな。
なんてこと言う奴はrcを使えってことか
0676名無しさん@お腹いっぱい。
2009/04/29(水) 05:58:40> v7は「現存」しない。
うちでは fpga で作った pdp-11 互換 CPU 上で元気に動いてますけど
0677名無しさん@お腹いっぱい。
2009/04/29(水) 05:59:43寝言は寝てから言ってね
0678名無しさん@お腹いっぱい。
2009/04/29(水) 06:49:51それを「現存しない」と言う。よく覚えとけ。
0679名無しさん@お腹いっぱい。
2009/04/29(水) 06:55:31/usr/bin/cd コマンドが存在するし。
多分、親プロセスを chdir()するようなシステムコール? を呼んでる??
0680名無しさん@お腹いっぱい。
2009/04/29(水) 08:08:58それたしか、自分がcdするだけ、ってnopコマンドじゃなかった?
0681名無しさん@お腹いっぱい。
2009/04/29(水) 08:30:530682名無しさん@お腹いっぱい。
2009/04/29(水) 08:59:20/usr/bin/cd fooとやると変わらないだろ。
/usr/bin/cd foo && cd fooでフェイルセイフなcdだ。
0683名無しさん@お腹いっぱい。
2009/04/29(水) 09:09:49> /usr/bin/cd foo && cd fooでフェイルセイフなcdだ。
それ、フェイルな場合 /usr/bin/cd の段階でフェイルして、
エラーメッセージも出てしまうから、ちっともフェイルセーフじゃない。
単に cd foo とやったのと何ら変わりない。
0684名無しさん@お腹いっぱい。
2009/04/29(水) 09:28:49> 多分、親プロセスを chdir()するようなシステムコール? を呼んでる??
そんなシステムコールあるわけねーだろw
0685名無しさん@お腹いっぱい。
2009/04/29(水) 09:39:00子プロセスとカレントディレクトリを共有する仕様にしようと思ってる。
cdを外付けコマンドにしたシェルを実装するんだ。
0686名無しさん@お腹いっぱい。
2009/04/29(水) 09:40:040687名無しさん@お腹いっぱい。
2009/04/29(水) 09:43:410688名無しさん@お腹いっぱい。
2009/04/29(水) 09:47:47それ、cdのときだけSHARE_CWDをセットするんなら、cdだけ特別扱い変わらず?
0689名無しさん@お腹いっぱい。
2009/04/29(水) 09:54:110690名無しさん@お腹いっぱい。
2009/04/29(水) 11:02:21既存のシェルスクリプトの話をしろよ
0691名無しさん@お腹いっぱい。
2009/04/29(水) 15:12:02貝料理のことじゃないよ。誰も料理の話はしていないよ。
0692名無しさん@お腹いっぱい。
2009/04/29(水) 16:36:320693名無しさん@お腹いっぱい。
2009/04/29(水) 22:08:15日本語が不自由な人か?
ちゃんと、未だにPDP-11互換CPU上でV7が動いているんだから
「現存しない」とは言わないと思うぞ
実際にV7が動いてるわけだから
0694名無しさん@お腹いっぱい。
2009/04/29(水) 22:14:28testやechoが外部になってるシェルの議論なんてしても無駄。
0695名無しさん@お腹いっぱい。
2009/04/29(水) 22:20:02(shは伝説的変態だが)
実際使うとすぐ落ちるんじゃないか?
0696名無しさん@お腹いっぱい。
2009/04/29(水) 22:24:31現実に動作してるじゃん
それを使う機械の有無は別として
シーラカンスは現存してないものって言ってるのに等しい
0697名無しさん@お腹いっぱい。
2009/04/29(水) 22:26:47はあ?
普通に使ってますけど?
0698名無しさん@お腹いっぱい。
2009/04/29(水) 22:29:24情報交換なので、個人的な趣味で過去の機械を動かして満足してる人は
無理に話題に入ってこないでいただけるかな?
0699名無しさん@お腹いっぱい。
2009/04/29(水) 22:46:360700名無しさん@お腹いっぱい。
2009/04/29(水) 23:49:380701名無しさん@お腹いっぱい。
2009/04/30(木) 01:08:37いい加減にしてね(はーと)。
0702名無しさん@お腹いっぱい。
2009/04/30(木) 01:10:34じゃあ、ネタ振ってよ はーと
0703名無しさん@お腹いっぱい。
2009/04/30(木) 11:45:55> このスレは、実際にシェルスクリプトを使って仕事をしている人同士の情報交換
ダウト。
>>1には、お勉強・自慢・腕試しと明記されている。従って、趣味でシェルスクリプ
トを勉強している人もOKだろ。
0704名無しさん@お腹いっぱい。
2009/04/30(木) 18:33:490705名無しさん@お腹いっぱい。
2009/04/30(木) 18:40:44getoptは外出しの別物
0706名無しさん@お腹いっぱい。
2009/04/30(木) 18:42:07オプションの処理なんてたかが知れてるし
その他の面でもgetoptsのほうがいいの?
0707名無しさん@お腹いっぱい。
2009/04/30(木) 18:46:55getoptsならもちろん無問題だけど。
0708名無しさん@お腹いっぱい。
2009/05/02(土) 01:11:20以下のような形式のログ(MySQLのバイナリログみたいなもの)があるとします。
---------------------
09/04/01 12:00 aaa
09/04/01 13:10 aaa
bbb
09/04/01 13:20 ccc
09/04/01 14:40 aaa
ddd
eee
ccc
---------------------
このような形式のログから、例えば、13時台のログを抜き出すにはどうしたらいいでしょうか?
上記のログでいうと、以下のような結果がほしいです。
---------------------
09/04/01 13:10 aaa
bbb
09/04/01 13:20 ccc
---------------------
awkとgrepを駆使すればできそうなのですが、いまいちわかりません。
よろしくご教示お願いします。
0709名無しさん@お腹いっぱい。
2009/05/02(土) 02:24:32aaa や bbb がスペースやTABで分割された複数の単語(bbb = xxx xxx ...)になることが無いなら
awk 'NF>2{if($2~/^13:[0-5][0-9]$/)f=1;else f=0}f==1' "ログファイル"
複数の単語になるが bbb = xxx 00:00 xxx ... みたいに2番目の要素が時刻形式になることが絶対無いなら
awk '$2~/^[0-9][0-9]:[0-9][0-9]$/{if($2~/^13:[0-5][0-9]$/)f=1;else f=0}f==1' "ログファイル"
でいけそうな気がする。手元に環境ないんで試せてないけど
0710名無しさん@お腹いっぱい。
2009/05/02(土) 02:35:06cat 2ch-708-20090501.data | grep "[0-9]\{2\}/[0-9]\{2\}/[0-9]\{2\} 13:"
冗長的に書くとこんな感じ
cat 2ch-708-20090501.data | grep "[0-9][0-9]/[0-9][0-9]/[0-9][0-9] 13:"
man grepを読んで、cat data | grep うんたらで正規表現を試すといいと思うよ
0711名無しさん@お腹いっぱい。
2009/05/02(土) 04:37:380712名無しさん@お腹いっぱい。
2009/05/02(土) 06:34:290713名無しさん@お腹いっぱい。
2009/05/02(土) 09:33:07これはどう?
sed -n '/^[0-9]/h; x; /13:[0-5][0-9]/{x;p}' datafile
0714名無しさん@お腹いっぱい。
2009/05/02(土) 09:44:160715名無しさん@お腹いっぱい。
2009/05/02(土) 10:01:29どうせならシェルでやれ。内部コマンドだけがいいぞ。
#!/bin/sh
f=0
while IFS= read line
do
case $line in *' 13:'??' '*) f=1;; 0*) f=0;; esac
case $f in 1) echo "$line";; esac
done
0716名無しさん@お腹いっぱい。
2009/05/02(土) 14:07:550717名無しさん@お腹いっぱい。
2009/05/02(土) 15:35:400718名無しさん@お腹いっぱい。
2009/05/02(土) 15:45:29元の質問読んでる?
この場合は処理が行内で完結せず、
状態をどこかに記憶しなければならないのでフラグは必須なんだが。
>>713 の sedでは、状態をホールドスペースに記憶して、
ホールドスペースある同じ文字列を毎回正規表現で比較し直すという、
フラグよりダサイことをやってる。
0719名無しさん@お腹いっぱい。
2009/05/02(土) 15:57:25whileループが2重になってしまうし、
条件判断や echo が複数箇所で必要になって余計わかりにくい。
>>715 のフラグ方式の方がいいな。
#!/bin/sh
while IFS= read line
do
case $line in
*' 13:'??' '*)
echo "$line"
while IFS= read line
do
case $line in *' 13:'??' '*);; 0*) break;; esac
echo "$line"
done;;
esac
done
0720名無しさん@お腹いっぱい。
2009/05/03(日) 13:26:32>>718 はありえない。毎回正規表現で比較するコストなんてたいしたことない
0721名無しさん@お腹いっぱい。
2009/05/03(日) 17:43:17質問者はawkとgrepを駆使するつもりだったようなので
>どうせならシェルでやれ。内部コマンドだけがいいぞ。
と強制する必要はないんじゃない?理由もイミフだし。
内部コマンドで済ます事に拘ることで >>715 とワザワザ長ったらしくしながら
大きなアドバンテージがある訳でもなく、もし、aaa とかが "13:xx " や "xxx xxx xxx 13:xx xxx ..." とか有り得るなら
>>709 では問題にならないのに >>715(や>>719も) では上手くいかない事を説明してないし
0722721
2009/05/03(日) 17:47:09○ もし、aaa とか bbb とかが、 "13:xx " や "xxx xxx xxx 13:xx xxx ..." という値を取りうるなら
0723名無しさん@お腹いっぱい。
2009/05/03(日) 21:03:55昨晩、実行されたのですが・・・エラーになりました。何が悪いのでしょうか?
shell
----- ここから -----
#!/bin/sh
find ./public_html/hoge/dat/ -type f -atime +1 -exec rm {} \;
----- ここまで -----
エラーメッセージ
/virtual/hoge/public_html/hoge.sh: line 2:
: command not found
find: missing argument to `-exec'
0724名無しさん@お腹いっぱい。
2009/05/03(日) 21:26:12ぱっと見、怖いスクリプト書いてるな…
一般的な助言しとくと
・カレントディレクトリは明示的に指定しる。
・cron実行時のPATH環境はどうなってる?
(要はfindとrmコマンドは、フルパスでなくても実行可能?)
・このスクリプトに実行権与えてる?
0725名無しさん@お腹いっぱい。
2009/05/03(日) 21:57:18ご指摘ありがとうございます。カレントは「具象化」します。
実行エラーの原因がわかりました。
改行コードが「CRLF」でした _| ̄|○ il||l
「LF」のみして正しく動作しました。
0726名無しさん@お腹いっぱい。
2009/05/03(日) 22:45:14おおげさな「助言」が全く関係ないオチだったね。
エスパー失格。
0727名無しさん@お腹いっぱい。
2009/05/03(日) 23:10:48おまえ何様のつもり?
>>724の指摘内容は、cronでスクリプトが動かないときに
真っ先に疑うべき内容で、別に大げさな助言でもなかろうに。
善意で答えている人に対して失礼すぎ。
0728名無しさん@お腹いっぱい。
2009/05/03(日) 23:37:25指摘は当たらないと意味がない。
もっと >>723 の質問をよく見ろよ。問題の鍵はここにあるんだよ。
>>723 の 「find: missing argument to `-exec'」のエラーメッセージ。
これが大きな鍵だ。
試しに、
find . -exec echo {} ?^M
って実行してみると良い。^Mは、Ctrl-[V] + [M] の CR な。
find: missing argument to `-exec'
って出るだろ。
あと、>>723 の「: command not found」
これは、#!/bin/sh^M になってる時出るメッセージだ。
以上を知ってれば的確な答えはすぐに出せたはず。
0729名無しさん@お腹いっぱい。
2009/05/04(月) 00:28:45たとえば、
find . -type f -name \*php
と
find . -name \*php -type f
は、実行速度に違いとかありますでしょうか?
0730名無しさん@お腹いっぱい。
2009/05/04(月) 01:01:09選択性の高いものを先に書いたほうが早い。
例えば比率的に「ファイル数:ディレクトリ数=9:1」で「*phpの数:*php以外の数=1:9」なら -name \*php を先に書いたほうが、90%の確率で -type f を評価する必要がなくなるので、逆に書くよりは高速になる可能性が高くなる。
要は、対象になるディレクトリの中身がどうなる傾向にあるかに拠る。
ただ、-type f単体の評価のほうが-nameより高速であろうから、単純に比率だけからでは一概には言えない。実際に計測して統計とるのが最善。
0731名無しさん@お腹いっぱい。
2009/05/04(月) 01:01:140732名無しさん@お腹いっぱい。
2009/05/04(月) 06:14:41>>730は釣り。
0733名無しさん@お腹いっぱい。
2009/05/04(月) 09:02:34処理するつくりになってると思い込んでいるんだろう
そういう仮定(本人は仮定とは思ってない)が書かれないから、他人には
全く理解不能になってしまう。
0734名無しさん@お腹いっぱい。
2009/05/04(月) 11:06:23従って指定順に処理されると考えるのが普通。
>>733はエスパーしたいなら、エスパースレにいけ。放置された質問たまってるぞ。
0735名無しさん@お腹いっぱい。
2009/05/04(月) 13:24:43http://www.linux.or.jp/JM/html/GNU_findutils/man1/find.1.html
find は与えられたファイル名以下のディレクトリツリーを検索し、同じく与えられた評価式を左から右に向かって優先順位の高いものから評価する (演算子のセクションを見よ)。
評価式の結果が確定すると (and の左項が偽だったり、 or の左項が真だった場合など) 評価は終了し、引き続き次のファイル名が評価される。
0736名無しさん@お腹いっぱい。
2009/05/04(月) 14:18:220737名無しさん@お腹いっぱい。
2009/05/04(月) 14:45:250738名無しさん@お腹いっぱい。
2009/05/04(月) 19:50:010739名無しさん@お腹いっぱい。
2009/05/05(火) 00:38:14違ったらごめんw
0740名無しさん@お腹いっぱい。
2009/05/05(火) 06:57:49(変わらないケースもまれにある)
0741名無しさん@お腹いっぱい。
2009/05/05(火) 22:31:34どちらの言ってることが正しいか自分の環境でテストしてみたんだけど、
1回目のfindでは7秒かかってたのが、2回目以降では、0.5秒で検索できた。
これって、OSがfindの結果をキャッシュしてるってことかなぁ、、、
テストするために、そのキャッシュをクリアする方法ってある?
ご存知の方は教えてくださいm(_ _)m
0742名無しさん@お腹いっぱい。
2009/05/05(火) 22:36:20それ、find自体は関係なくて、OSのファイルシステム(またはブロックデバイス)の
キャッシュが効いてるのが原因。
キャッシュの影響をなくすひとつの方法は、
umountできるディレクトリなら、
findの直前に一旦umountして、mountして、その直後に1回だけfindを実行する。
次のfindの前にまたumount/mountする。ちょっと面倒だけど。
0743名無しさん@お腹いっぱい。
2009/05/05(火) 22:36:22time sh first.sh; time sh second.sh;time sh first.sh; time sh second.sh;time sh first.sh; time sh second.sh;
おれは、こうしゃうけどね
でも、キャッシュはしていないと思うけどね
0744名無しさん@お腹いっぱい。
2009/05/05(火) 22:37:160746名無しさん@お腹いっぱい。
2009/05/06(水) 13:37:32で、テスト結果は?
0747>>741
2009/05/06(水) 23:56:33改行多すぎてはれないので、二回にわけてはるね。
■前提条件
~$ uname -r
5.4-RELEASE-p6
~$ find . -type f | wc -l
30374
~$ find . -type d | wc -l
1130
~$ find . -type l | wc -l
445
~$ find . -type f -name \*php | wc -l
137
■テストの手順
(1) time find . -type f -name \*php
(2) shutdown -p nowした後、マシン起動
(3) time find . -name \*php -type f
(4) shutdown -p nowした後、マシン起動
(5) time find . -name \*php -type f
(6) shutdown -p nowした後、マシン起動
(7) time find . -type f -name \*php
0748>>741
2009/05/06(水) 23:57:41■結果
(1)
real 0m7.697s
user 0m0.181s
sys 0m0.261s
(3)
real 0m7.205s
user 0m0.207s
sys 0m0.238s
(5)
real 0m7.229s
user 0m0.187s
sys 0m0.258s
(7)
real 0m8.164s
user 0m0.199s
sys 0m0.243s
find . -type f -name \*phpより、find . -name \*php -type fのようがはやい。
■結論
オプションの並びをかえると、実行速度はかわる
0749名無しさん@お腹いっぱい。
2009/05/07(木) 01:03:510750名無しさん@お腹いっぱい。
2009/05/07(木) 03:47:590751名無しさん@お腹いっぱい。
2009/05/07(木) 04:25:05> /dev/nullしろよ。
それじゃreal timeが表示の時間測ってることになるだろ。
0752名無しさん@お腹いっぱい。
2009/05/07(木) 06:10:32stat(2)は減らない。
1回のstat(2)で、ファイル名とファイルタイプは同時に得られるから。
0753名無しさん@お腹いっぱい。
2009/05/07(木) 10:03:02GNU の ls や stat を使うように書かれたシェルスクリプトを実行するために、
alias ls='gls'
alias stat='gstat'
を設定した状態で実行したいんですが、シェルでセットしてもシェルスクリプト中では有効になりません。
0754名無しさん@お腹いっぱい。
2009/05/07(木) 10:29:17stat(2)でファイル名得られるってどういうOSですかあ?
0755名無しさん@お腹いっぱい。
2009/05/07(木) 10:55:10readdir()等でファイル名一覧が得られたあと、
-type f のオプションがあるかどうかにかかわらず、
findとしてはそのディレクトリのすべてのファイルについて
一旦 stat(2)を実行する。
(でないと、サブディレクトリかどうかとかの判断ができないから)
で、-type fオプションとかがあると、すでに実行済みの stat(2)の結果を
利用するから、stat(2)の実行回数は増えない。
0756名無しさん@お腹いっぱい。
2009/05/07(木) 11:10:07PATHの一番優先順位のところに自作のディレクトリでもつくって、通して、ls,statというファイル名をつくって
$ cat ls
#/bin/sh
gls $@
$ cat stat
#/bin/sh
gstat $@
みたいなことするんじゃないの?
即席なんでこういうのは、ダサ過ぎなのかもしれないけど
0757名無しさん@お腹いっぱい。
2009/05/07(木) 11:45:08> (でないと、サブディレクトリかどうかとかの判断ができないから)
ディレクトリの判定には、いきなりchdirしてみると言う方法も考えられるので却下。
0758名無しさん@お腹いっぱい。
2009/05/07(木) 11:47:34実際のfindではいきなりchdirはしないから却下
0759名無しさん@お腹いっぱい。
2009/05/07(木) 11:48:26bashがスクリプト中でalias使えるか知らないが、
#!/bin/bash
alias ls='gls'
alias stat='gstat'
. そのシェルスクリプト
0760名無しさん@お腹いっぱい。
2009/05/07(木) 11:49:17そのような実装のfindが無いことを証明出来るなら採用。
0761名無しさん@お腹いっぱい。
2009/05/07(木) 11:51:39いきなりchdirすると、それがディレクトリへのシンボリックリンクだった場合に困る。
(-followオプションを付けていないのにsymlinkをたどっては行けないから)
だからいきなりchdirする実装はあり得ない。
0762名無しさん@お腹いっぱい。
2009/05/07(木) 12:29:240763名無しさん@お腹いっぱい。
2009/05/07(木) 12:35:530764名無しさん@お腹いっぱい。
2009/05/07(木) 16:42:560765名無しさん@お腹いっぱい。
2009/05/07(木) 18:24:100766名無しさん@お腹いっぱい。
2009/05/07(木) 18:24:380767名無しさん@お腹いっぱい。
2009/05/07(木) 18:53:47ttp://www.youtube.com/watch?v=h2mnwAJJDxY
ttp://www.youtube.com/watch?v=bfEVoJhxPPE
0768名無しさん@お腹いっぱい。
2009/05/07(木) 23:15:48readdir()すると、
ファイル名とi-node番号だけじゃなくて、
struct direntの中でファイルタイプも返すUNIXがある。
e.g. FreeBSD
0769名無しさん@お腹いっぱい。
2009/05/07(木) 23:20:140770名無しさん@お腹いっぱい。
2009/05/08(金) 04:01:23お前ら、レベル高いな、、、
ぜんぜん、この話題ついていけねぇ、、、orz
どうやったら、そんな高いレベルになるのか教えてくれ。
何かお勧めの本とか。
0771名無しさん@お腹いっぱい。
2009/05/08(金) 06:55:30その場合はreaddir()だけでファイル名もファイルタイプも得られるから、
findの-nameや-type fの有無にかかわらず、結局stat(2)の回数は増えない、でFA?
■ このスレッドは過去ログ倉庫に格納されています