トップページunix
1001コメント301KB

シェルスクリプト総合 その7

■ このスレッドは過去ログ倉庫に格納されています
0001ミスターシェル2006/09/07(木) 13:00:11
シェルスクリプトの総合スレです。
スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>1-6くらい)をご覧ください。


□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
 bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
 Linuxユーザは/bin/shの正体がbashなので特に注意。
・csh/tcshのシェルスクリプトは推奨されません。
(理由は「csh-whynot」でググれ)
・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。
 manや参考リンクを見ましょう。
 aproposないしはman -kでそれらしい単語による簡単な検索もできます。
・シェルスクリプトのことをシェルってゆーな
・シェルで使えるワイルドカード等は正規表現ではありません。
正規表現の話題はスレ違い(正規表現スレへ)

□初心者へのアドバイス:
・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも
 RubyやPerlの方が適した仕事には素直にそちらを使いましょう。
・知らないコマンドが出てきたらmanを引きましょう。
・思い通りに動かないときは、まずは sh -x でトレースしましょう。
0638名無しさん@お腹いっぱい。2006/12/21(木) 00:37:34
>>637
-vはFreeBSD限定のような気がする
0639名無しさん@お腹いっぱい。2006/12/21(木) 02:39:21
>>594
rm `ls -t /directory/*.log | tail -n +11`
なんて方法もあるかなと。
0640名無しさん@お腹いっぱい。2006/12/21(木) 08:04:44
>>639
それいけるのか?
1日に複数個ログはいても
0641名無しさん@お腹いっぱい。2006/12/21(木) 16:23:03
date は1日引くのができるんじゃないっけ?
ago だかなんかの引数で
0642名無しさん@お腹いっぱい。2006/12/21(木) 16:29:37
>>641
FreeBSDのdateは無理です
0643名無しさん@お腹いっぱい。2006/12/21(木) 16:36:30
FedoraBSDって無いんですか?
0644名無しさん@お腹いっぱい。2006/12/21(木) 18:41:38
kFreeBSDならあってもおかしくない気がするよな
0645名無しさん@お腹いっぱい。2006/12/23(土) 04:10:11
大量のファイルに対して、
foo *
というようなことをやりたいのですが、arg list too longというような
メッセージが出ます。
で、有名な
echo * | xargs foo
で解決と思いきや、xargsってARG_MAXを超えた引数については、fooを呼びなおすみたいですね。
fooは全てのファイルに対して1回だけ実行されないと困るんですが、何かいい方法はあるでしょうか?
0646名無しさん@お腹いっぱい。2006/12/23(土) 09:36:47
foo を改造するしかない
0647名無しさん@お腹いっぱい。2006/12/23(土) 09:45:32
>fooは全てのファイルに対して1回だけ実行
xargs でいいじゃん
0648名無しさん@お腹いっぱい。2006/12/23(土) 10:11:40
全てのファイル <-> それぞれのファイル
0649名無しさん@お腹いっぱい。2006/12/23(土) 10:27:41
>>645
1 fooをshell functionで書き直す。
2 arglist広げてシステム再構築。
3 fooを複数回実行されても困らないよう作り直す。
0650名無しさん@お腹いっぱい。2006/12/23(土) 10:31:37
まずはオナヌーして落ち着く
0651名無しさん@お腹いっぱい。2006/12/23(土) 11:34:05


 find ... | xargs tar cvf hoge.tar

で ARG_MAX にあふれたファイルだけ入ったアーカイブ作ったことある。

たぶん受け側のプログラムを標準入力からファイル一覧読むように
改造するしかない。
0652名無しさん@お腹いっぱい。2006/12/23(土) 12:20:28
>>647
欲嫁。
>xargsってARG_MAXを超えた引数については、fooを呼びなおす
0653名無しさん@お腹いっぱい。2006/12/23(土) 12:25:48
xargs -n 100 とかでおk、とかそういう話ではない?
0654名無しさん@お腹いっぱい。2006/12/23(土) 12:37:58
>>652
xargsでいいじゃん
0655名無しさん@お腹いっぱい。2006/12/23(土) 12:54:36
>>654
欲嫁。
>xargsってARG_MAXを超えた引数については、fooを呼びなおす
0656名無しさん@お腹いっぱい。2006/12/23(土) 13:05:47
>>645の書き方がいまいち曖昧なんだが
fooで同じファイルを二度処理するのはまずいってだけの話なら
xargsでいいんでないの?

一度のfooの実行で全ファイルを処理したいっつーんなら、
引数渡しでやるのは無理だわなそりゃ
06576452006/12/23(土) 13:58:06
色々レスありがとうございます。

書き方が悪かったですが、fooは複数回実行されると困るというよりも、
全ファイルのデータを読み込んだ上でまあ数値計算みたいなものなんですが、
計算を行いたいわけです。
一度に処理されないと意味がない、ということです。

>>653
これ試してみましたけど、-n 100000とかやってもARG_MAXの壁は越えられないんで駄目でした。

どうもfoo内でフャ@イル一覧を読b゙ように書き換bヲるか、再構築bチてことみたいbナすね。
0658名無しさん@お腹いっぱい。2006/12/23(土) 14:02:43
ガベージイン
ガベージアウト
0659名無しさん@お腹いっぱい。2006/12/23(土) 15:50:07
引数の個数に1万か3万の制限があったような
0660名無しさん@お腹いっぱい。2006/12/23(土) 15:58:38
xargsって言ってる奴あほ過ぎる
0661名無しさん@お腹いっぱい。2006/12/23(土) 16:19:21
だから >>651 だってのに。
受け側のプログラムが引数使わないように修正する以外に方法はないって。
0662名無しさん@お腹いっぱい。2006/12/23(土) 22:29:44
xargsは obsolete。別の意味でも使ってはいけない。
0663名無しさん@お腹いっぱい。2006/12/23(土) 22:44:11
また湧いた
0664名無しさん@お腹いっぱい。2006/12/23(土) 22:50:02
>>662
kwsk
0665名無しさん@お腹いっぱい。2006/12/25(月) 11:59:27
find . -exec foo {} \;
0666名無しさん@お腹いっぱい。2006/12/25(月) 12:02:43
>>665
0点
0667名無しさん@お腹いっぱい。2006/12/25(月) 12:06:06
>>665
100点
0668名無しさん@お腹いっぱい。2006/12/25(月) 12:16:32
良く読んでない奴多いね。
foo file1 file2 .... (すべてのファイル)
みたいに、1回だけで実行したいという質問なので、
>>665 は 0点。
0669名無しさん@お腹いっぱい。2006/12/25(月) 12:26:59
んなことはみんなわかってる
0670名無しさん@お腹いっぱい。2006/12/25(月) 13:13:18
* じゃないしな。
0671名無しさん@お腹いっぱい。2006/12/25(月) 17:00:20
foo のbinary ラッパーを書いて、main を直接呼び出す。

普通はプログラムを書き直すよな...
0672名無しさん@お腹いっぱい。2006/12/27(水) 19:02:58
汎化してGPLで公開すればよくね?
0673名無しさん@お腹いっぱい。2006/12/28(木) 02:21:04
ARG_MAXってシェルの制限ではなくてint argc, char **argvのホントの
上限なんだね。標準入力からN行読み込んでexecvpで直接渡すようなの
書いてみたら32768でアウト。

任意のコマンドのmainをフックして、単なる関数として渡すような形で
汎化しないと駄目だな。もっとも問題のプログラムが処理対象のリストを
標準入力から読んだほうがよほどいいと思うけど。
0674名無しさん@お腹いっぱい。2006/12/28(木) 08:02:05
(ものすごくつまらない)ネタだと思ってたらマジで汎化してGPLとかほざいてたのか
0675名無しさん@お腹いっぱい。2006/12/28(木) 17:30:54
いや検討まで含めてネタだろ

バカか?
0676名無しさん@お腹いっぱい。2006/12/28(木) 19:40:53
gdb でmain で止めて、

レジスタ渡し、あるいはスタック渡しの部分で、malloc()で上書き。

.gdbinit で、がんがん代入。

実行開始。

ってのは、どう?
0677名無しさん@お腹いっぱい。2006/12/28(木) 21:21:09
こんなこと書いてるうちに元コード直し終わると思うけど、
-Dmain=xmain して、別途 dlsym(h, "xmain"); で呼び出すとか。
mainをそのまま直接呼ぶ方法あるのかな?ptraceとか使える?
0678名無しさん@お腹いっぱい。2006/12/28(木) 21:27:02
変わったシェルを使ってるんですね
0679名無しさん@お腹いっぱい。2006/12/28(木) 22:49:41
#!/bin/sh

( cat <<EOFEOF
extern int printf(const char *,...);
main(int ac,char *argv[])
{
int i;
for(i=0;i<ac;i++)
printf("%d %s¥n",i,argv[i]);
}
EOFEOF
) | gcc -x c -
(
echo "b *main"
echo "run"
echo 'set $r3=3'
echo 'set *((char **)$r4) = "test1"'
echo 'set *((char **)$r4+1) = "test2"'
echo 'set *((char **)$r4+2) = "test3"'
echo 'set *((char **)$r4+3) = "test4"'
echo " c" ) | gdb ./a.out

(Mac OS X 限定...)
ってな感じで、symbol table がある場合は可能らしい。strip されている場合は、
entry point を根性で探してくれ。
0680名無しさん@お腹いっぱい。2006/12/28(木) 22:52:07
おっと、
  set $r4=(void *)malloc(1000)
とかで malloc() するのを忘れずに。
0681名無しさん@お腹いっぱい。2006/12/29(金) 11:23:43
独自rtld書いてmain呼び出す。
0682名無しさん@お腹いっぱい。2006/12/29(金) 12:24:23
stripされててもmainのオフセット位置はどのプログラムでも同じ(同じ
環境なら)だから、そちらで調べた値をreadelfで得たエントリポイント
アドレスに足せばgdbのブレークポイント設定位置が判るよ。

後は$espから引数にアクセスして書きかえればいい。
この一連の操作はシェルスクリプトにできるから、xargsで扱えない
例外状況の処理用ツールになる(かもしれない)。
0683名無しさん@お腹いっぱい。2006/12/29(金) 19:38:07
ファイルをそのパスを含んだファイル名にしてコピーしたいです。
例えば ./foo/bar/mbox を ./foo_bar_mbox にコピーしたい。

find . -type f -name mbox -exec cp {} `echo {}|tr '\/' '_'` \;
とかしても
cp: ./foo/bar/mbox and ./foo/bar/mbox are identical (not copied).とエラーになっちゃう。

スレ違いだったら誘導して
0684名無しさん@お腹いっぱい。2006/12/29(金) 19:46:12
>>683
シェルが先に ` ` を評価するから
実際に実行されるのは
find . -type f -name mbox -exec cp {} {} \;
になるな。
0685名無しさん@お腹いっぱい。2006/12/29(金) 20:11:47
for f in `find . -type f -name mbox`;do cp $f `echo $f | tr '\/' '_'`;done
0686名無しさん@お腹いっぱい。2006/12/29(金) 20:27:54
無理に one-liner にしなくてもいいんじゃね?
0687名無しさん@お腹いっぱい。2006/12/29(金) 20:30:28
find . -type f -name mbox | sed 'p;s,/,_,' |xargs -n2 cp
0688名無しさん@お腹いっぱい。2006/12/29(金) 21:24:58
ありがとん。
>>685の方法で出来た。
0689名無しさん@お腹いっぱい。2006/12/29(金) 21:30:16
ねーねー
このスレのまとめって無いのかなぁ
ここしばらくの話、めっちゃ勉強になるんですけど
0690名無しさん@お腹いっぱい。2006/12/29(金) 21:32:30
わざわざ別解書いた >>687 が不憫。
それ以前の問題で外してしまった >>686 はもっと不憫。
0691名無しさん@お腹いっぱい。2006/12/29(金) 21:36:01
/usr/fbin
0692名無しさん@お腹いっぱい。2006/12/29(金) 22:02:53
>>690
>>687はエラー出ちゃうんだよ。
どうも最初の/しか置換されなくて._foo/bar/mboxにコピーしようするの。
で原因なんだろうと調べてみてるんだけどわけわからん。
0693名無しさん@お腹いっぱい。2006/12/29(金) 22:13:46
>>689
まとめてクレクレ
0694名無しさん@お腹いっぱい。2006/12/29(金) 22:48:30
>>692
多分 sed の部分が間違っているんじゃないかな
検証してないけど
0695名無しさん@お腹いっぱい。2006/12/29(金) 22:49:13
sedのところで、g が抜けてる。
0696名無しさん@お腹いっぱい。2006/12/29(金) 23:55:59
#!bash2.04

echo "$(echo " $(echo "`echo "$(ps)"`")")"

ちょい大げさに書きすぎたけど、こんな感じのを見かけた
これはどんな決まりでトークンに切り分けてるのさ!!
0697名無しさん@お腹いっぱい。2007/01/01(月) 00:02:28
新年キタコレ
今年もよろ69
0698名無しさん@お腹いっぱい。2007/01/01(月) 01:30:45
よろむく?
0699名無しさん@お腹いっぱい。2007/01/01(月) 02:23:40
>>698
Yellow Mookだろ。
それくらい察してやってくれ。
0700名無しさん@お腹いっぱい。2007/01/02(火) 11:18:36
>>699
素で分からんかったorz
0701名無しさん@お腹いっぱい。2007/01/03(水) 19:21:38
4649?
0702名無しさん@お腹いっぱい。2007/01/03(水) 19:36:33
よろシックスナイン
0703名無しさん@お腹いっぱい。2007/01/09(火) 22:08:18
0704名無しさん@お腹いっぱい。2007/01/10(水) 15:53:22
find で得られたファイルを更新時間順で並べる方法があれば教えていただきたい。
0705名無しさん@お腹いっぱい。2007/01/10(水) 15:59:18
ls -t `find hoge....`
0706名無しさん@お腹いっぱい。2007/01/11(木) 20:32:03
それでいいのか…
失礼しました
0707名無しさん@お腹いっぱい。2007/01/11(木) 22:03:31
argument too long
0708名無しさん@お腹いっぱい。2007/01/12(金) 00:58:20
sedでhoge.datというファイルを編集するときに

sed 's/aaa/bbb/' hoge.dat > tmp.dat
mv tmp.dat hoge.dat

のようにして、一時的なファイルとしてtmp.datというファイルを用意するのが無駄だと
思うのですが、このような一時的なファイルを使わないで
直接、hoge.datをsedで編集する方法はありませんでしょうか?

m(_ _)m
0709名無しさん@お腹いっぱい。2007/01/12(金) 01:08:30
>>708
・-i オプションをつかう
・-i オプションがないsedをつかっているなら in-place editing 対応の sed の導入を検討する
・ed をつかう
・perl を使う
・一時ファイルを必要としないファイル命名法を検討する

お好みで
0710名無しさん@お腹いっぱい。2007/01/12(金) 01:10:26
GNU sedには-iオプションがある。BSDのもある。

あとinplaceというあらゆるフィルタをin-place editに使うコマンド
もある。(これはぐぐって)
0711名無しさん@お腹いっぱい。2007/01/12(金) 01:20:43
FreeBSD の標準 sed に -i オプションが取り入れられたのは 4.7R でそれ以前には無い。
NetBSD の標準 sed には -i オプションは無い。 
OpenBSDの(ry
以前のGNU sed には in-place editing 機能はない (Changelog によると 2001-09-25 )
0712名無しさん@お腹いっぱい。2007/01/12(金) 01:29:18
MacOSXのBSD由来のsedにはあるみたいだな。いつのだろ。
0713名無しさん@お腹いっぱい。2007/01/12(金) 01:33:43
>>710
http://www.idaemons.org/projects/inplace/
へー、これか。
これは知らなかった。
0714名無しさん@お腹いっぱい。2007/01/12(金) 01:35:17
>>712
MacOSXのFreeBSDなユーザーランドは 最初は FreeBSD 3.xR由来で
そのうちに 5.x由来とかのになってそれから先はシラネ。
でも5.x以降だろうなと推測。
0715名無しさん@お腹いっぱい。2007/01/12(金) 01:45:52
>>714
/usr/share/misc/bsd-family-tree あたりにちゃっかり書いてあったりする。
0716名無しさん@お腹いっぱい。2007/01/12(金) 01:52:33
そのディレクトリの中はじめて見た。
flowers(花言葉)とかbirthtoken(誕生石)とか、妙なファイルもあるんだな。
0717名無しさん@お腹いっぱい。2007/01/12(金) 01:55:54
>>716
お、ほんとだ
なんでもあるな
0718名無しさん@お腹いっぱい。2007/01/12(金) 01:57:43
>>715
なるほど
でもそのファイルにNeXTの話がないのが微妙
0719名無しさん@お腹いっぱい。2007/01/12(金) 17:59:42
>>708
UNIXのファイルシステム上だと
(rm hoge.dat; sed 's/aaa/bbb/' > hoge.dat) < hoge.dat
でOK
0720名無しさん@お腹いっぱい。2007/01/12(金) 18:11:02
>>719
なるほど
0721名無しさん@お腹いっぱい。2007/01/12(金) 18:31:57
ああ、なるほど。
0722名無しさん@お腹いっぱい。2007/01/12(金) 19:50:06
あ、アナルほど
0723名無しさん@お腹いっぱい。2007/01/12(金) 20:44:54
手順をトレースしてみました。こんな理解でよろしいでしょうか。

1. < hoge.dat
ファイルディスクリプタを確保する。(&標準入力にする)
このディスクリプタが開いている限り、ファイル名がなくなっても
ファイルの実体は消えない。
2. (...)
サブシェルを起動する。ファイルディスクリプタは継承される。
3. rm hoge.dat
hoge.datというディレクトリエントリ(ファイル名)を消す。
しかし、それが指していた実体は1.のディスクリプタからまだ参照
されているため、消えない。
4. >hoge.dat
別途hoge.dat という名前でファイルを開く。(&標準出力にする)
これは、もとのhoge.datが指していた実体とは別のディスク領域に書かれる。
5. 全体が終了すると、ファイルディスクリプタが閉じ、もとのファイル実体
が使っていたディスク領域が未使用状態とみなされる。
0724名無しさん@お腹いっぱい。2007/01/13(土) 01:27:34
>>719
(...)じゃなくて{...;}じゃダメ?
0725名無しさん@お腹いっぱい。2007/01/13(土) 02:33:57
>>724
自分でまずやってみろよ
0726名無しさん@お腹いっぱい。2007/01/14(日) 01:54:35
最近のシェルは`test`や`[`が組み込みコマンドになってるとのことで
確かにpowerpc-apple-darwin8.0のbash 2.05b.0(1)-releaseでは`builtin`で呼び出せることも確認しました
一方で/bin/[や/bin/testも存在し、当然実行可能でした
また、若干の差異はありますが、複合コマンド(compound command)の`[[ expression ]]`もほぼ同じ用途に利用できます

そこで質問なんですが、少し上で話されていたような処理の重さ(無駄なプロセスの生成等)を考えた場合、
どれを使うのがベストなんでしょう?
0727名無しさん@お腹いっぱい。2007/01/14(日) 02:03:12
当然内部コマンドでしょう。
つーか、パフォーマンス重視な場合は
シェルスクリプトなんか使うな。
0728名無しさん@お腹いっぱい。2007/01/15(月) 02:32:27
cat access.log | grep $URL | wc -l >> $FILE

という感じで、アクセスログから、特定のURLを探して数を数えて、その数をファイルに書き込んでいるのですが、これを毎日やると、
10
20
10
15
といった感じで、改行されてしまいます。
これを
10,20,10,15
という風に、,区切りにするにはどうすればよいでしょうか?
0729名無しさん@お腹いっぱい。2007/01/15(月) 03:50:32
>>728
tr -d '\n' < filea | sed -e 's/[ ]\{1,\}/,/g'
的な
0730名無しさん@お腹いっぱい。2007/01/15(月) 03:56:11
tr '¥n' ','
0731名無しさん@お腹いっぱい。2007/01/15(月) 03:56:54
おや、こんな時間にかぶるとは。
0732名無しさん@お腹いっぱい。2007/01/15(月) 06:03:29
sedについて質問です。
hoge.datというファイルがあるとして、
その中身が以下のようであったとします。
aaa
これを、
aaa
bbb
ccc
のように変更したいのです。
で、sedでこれを実現しようとする場合、
#!/bin/sh
sed 's/aaa/aaa \
bbb \
ccc/' hoge.dat
exit 0
こんな感じになると思います。
シングルコーテーションのとこがダブルコーテーションの場合は、
#!/bin/sh
sed "s/aaa/aaa \\
bbb \\
ccc/2 hoge.dat
exit 0
のように、\マークがふたつになると思います。
どうして、シングルコーテーションの場合は、\マークがひとつで、
ダブルコーテーションの場合は、\マークがふたついるのでしょうか?
よろしく呉教授お願いします。
0733名無しさん@お腹いっぱい。2007/01/15(月) 06:36:10
呉教授はいいねえ
呉智英?なんてね
0734sage2007/01/15(月) 09:21:26
>>733
リアルで引いた。
0735名無しさん@お腹いっぱい。2007/01/16(火) 23:37:40
ダブルクオートの場合は、一つめの\がシェルに持ってかれてる。
シングルクオートの場合はシェルに持ってかれないので\一つでOK。

こんな感じでシェルを介さなければ\一つでいける。
> cat replace.sed
s/aaa/aaa \
bbb \
ccc/
> sed -f replace.sed hoge.dat
aaa
bbb
ccc
>

SH(1)
> Double Quotes
> Enclosing characters within double quotes preserves the literal
> meaning of all characters except dollarsign (`$'), backquote
> (``'), and backslash (`\'). The backslash inside double quotes
> is historically weird. It remains literal unless it precedes the
> following characters, which it serves to quote:
> $ ` " \ \n
0736>>7322007/01/17(水) 05:11:59
>>735
ありがとうございますー
0737名無しさん@お腹いっぱい。2007/01/18(木) 05:23:42
質問させてください。

以下のHTMLファイルがあるとします。
------------------------
<html>
<body>
<!--
memo
-->
hoge
</body>
<html>
------------------------
これを、以下のように<!--から-->の部分を削除したいのです。
------------------------
<html>
<body>
hoge
</body>
<html>
------------------------

このようなことをしたい場合、シェルスクリプトではどのようにすればよいでしょうか?

ちなみに、うちの会社の吉田さん曰く「そんなのawk使えば一発だよ!」といってました。。
■ このスレッドは過去ログ倉庫に格納されています