トップページunix
987コメント345KB

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

■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。2008/10/16(木) 00:48:38
シェルスクリプトの総合スレです。
スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>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 でトレースしましょう。

前スレ
シェルスクリプト総合 その12
http://pc11.2ch.net/test/read.cgi/unix/1218277263/
0067名無しさん@お腹いっぱい。2008/10/23(木) 16:45:59
だからcase insensitiveでソートしろと。
0068名無しさん@お腹いっぱい。2008/10/23(木) 17:01:13
>>65
> L = line.split('.')
< L = line.lower().split('.')
0069名無しさん@お腹いっぱい。2008/10/23(木) 17:06:27
>>66
> 〜 x.split(〜
< 〜 x.downcase.split(〜

以上スレ違い話終了
0070名無しさん@お腹いっぱい。2008/10/24(金) 02:17:58
sort -n に対応する事をスクリプト言語でする時はどうするんかいな
0071名無しさん@お腹いっぱい。2008/10/24(金) 02:39:34
system()
0072名無しさん@お腹いっぱい。2008/10/24(金) 08:46:37
>>70
比較時に数値化(rubyなら to_iとか)
0073名無しさん@お腹いっぱい。2008/10/25(土) 01:06:59
%w|hoge100fuga hoge9fuga|.sort_by {|e|
[e.gsub(/\d+/, '#'), e.scan(/\d+/).map {|e| e.to_i}]
}
メンドクサ
0074名無しさん@お腹いっぱい。2008/10/25(土) 01:20:42
map {|e| e.to_i}は map(&:to_i) と書けるが、そのくらいかな、縮むのは。
0075名無しさん@お腹いっぱい。2008/10/25(土) 09:18:57
def embedded_numbers(s):
    pieces = re.split(r'(\d+)', s)
    pieces[1::2] = map(int, pieces[1::2])
    return pieces
ary.sort(key=embedded_numbers)
0076名無しさん@お腹いっぱい。2008/10/25(土) 21:56:24
ある文字列を,最後の1文字と,残りの前半部分の文字列に分けるにはどうすればいいですか?
最後の文字を変数 LAST_LETTER に,残りの文字列を FIRST_LETTERS に代入するように
してもらえるとありがたいです。
0077名無しさん@お腹いっぱい。2008/10/25(土) 22:03:53
s="foobarbaz"
expr "$s" : "\(.*\)."
expr "$s" : ".*\(.\)"
0078名無しさん@お腹いっぱい。2008/10/25(土) 22:12:09
>>76
方法は2つ

1。正規表現
2。文字列の長さを測って、1引いた長さと、その場所から1文字をとる

0079名無しさん@お腹いっぱい。2008/10/25(土) 22:20:34
>>77
惜しいね。そこまでわかってても、最後に LAST_LETTER FIRST_LETTERS に
代入するのができてないから、満点あげられないね。

>>78
77に具体的な答が(不完全だけど)出てるのに何言ってんだか。
具体的な答がないので点はあげられないね。
0080名無しさん@お腹いっぱい。2008/10/25(土) 22:29:44
S="foobarbaz"
FIRST_LETTERS=${S:0:${#S}-1}
LAST_LETTER=${S:${#S}-1}
0081名無しさん@お腹いっぱい。2008/10/25(土) 22:34:53
>>80
bash依存
0082名無しさん@お腹いっぱい。2008/10/25(土) 22:38:38
STRING=abcde

FIRST_LETTERS=${STRING%?}
LAST_LETTER=${STRING#"$FIRST_LETTERS"}

echo "$FIRST_LETTERS"
echo "$LAST_LETTER"
0083名無しさん@お腹いっぱい。2008/10/25(土) 22:50:35
>>78
>>82 は、正規表現も使っていないし、文字列の長さも測っていない。
よって、方法は3つ(以上)あることになる。
0084名無しさん@お腹いっぱい。2008/10/25(土) 22:52:58
スレ違いだがLEADING_LETTERSにしてくれ。
0085762008/10/25(土) 23:44:06
回答ありがとうございました。

>>84
LEADING_LETTERSの方が良かったですかね。
指摘ありがとうございます。
0086名無しさん@お腹いっぱい。2008/10/26(日) 00:14:49
>>81
正直、ここらの文字列操作はどれがbash拡張なのかどうか、
/bin/shがbashな環境にいると判別できないな。
0087名無しさん@お腹いっぱい。2008/10/26(日) 00:56:43
大手ECサイトのヨドバシドットコムが、サイトリニューアルから大規模な障害を3日間...
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail.php?qid=1220150877

506 :目のつけ所が名無しさん:2008/10/26(日) 00:47:20
大手ECサイトで、ここまで派手なリリース失敗は初めて見た。
エンジニア向けIT情報誌や関連サイトは、ぜひ取材して原因を明かして欲し
いは。
0088名無しさん@お腹いっぱい。2008/10/26(日) 08:36:19
>>86
UNIXで/bin/shがbashな環境なんてあるの?

あ、opensolarisとか??
0089名無しさん@お腹いっぱい。2008/10/26(日) 09:00:58
>>88
MacOS X
0090名無しさん@お腹いっぱい。2008/10/26(日) 11:02:55
>>88
LinuxはUNIXじゃないという煽りなんですね お疲れ様です
0091名無しさん@お腹いっぱい。2008/10/26(日) 12:57:35
>>90
LinuxをUNIXにしたらLinuxが嫌なんじゃない?
UNIXじゃ無いのがアレの誇りなんじゃないの?
0092名無しさん@お腹いっぱい。2008/10/26(日) 12:58:13
>>90
linuxというくくりでは/bin/shが何なのかは明確じゃないからな。
0093名無しさん@お腹いっぱい。2008/10/26(日) 13:45:38
Mac OS X はスレ違いだろ
0094名無しさん@お腹いっぱい。2008/10/26(日) 14:30:56
>>93
なぜ?
LinuxやFreeBSDと違って、れっきとしたUNIXだよ。
0095名無しさん@お腹いっぱい。2008/10/26(日) 14:43:57
アスファルトみたいな燃料じゃちょろちょろとも引火しないぜ
0096名無しさん@お腹いっぱい。2008/10/26(日) 21:27:04
前スレか前々スレあたりで、"$@" と書くべきか、${1+"$@"}と書くべきかって議論、
あったと思うけど、

zshで、setopt shwordsplit してる環境だと、
${1+"$@"} ではスペースが不本意に解釈されてしまって、引数がそのまま引き継げない。
やはり、単純明快に "$@" と書いた方が良いという結論。

$ zsh
zsh$ setopt shwordsplit
zsh$ set 'hoge boke'
zsh$ for i in ${1+"$@"}; do echo "$i"; done
hoge ← 2つに分かれてしまう
boke ← 2つに分かれてしまう
zsh$ for i in "$@"; do echo "$i"; done
hoge boke ← 正常
0097名無しさん@お腹いっぱい。2008/10/26(日) 21:35:29
>>96
> zshで、setopt shwordsplit してる環境だと、

当たり前やんw
すげえピンぼけ

0098名無しさん@お腹いっぱい。2008/10/26(日) 21:43:22
>>97
なんで当たり前?

zsh以外では setopt shwordsplit状態がデフォだよ。
それで、${1+"$@"} は "$@"と同様に動作する。

で、${1+"$@"} が、word split されるのは zshのバグと言える。
0099名無しさん@お腹いっぱい。2008/10/26(日) 22:50:20
findでファイル名に数字がつかわれてないものを引っ掛けたいんですが
-name のかっこいい正規表現でいけないですか?
0100名無しさん@お腹いっぱい。2008/10/26(日) 22:52:41
>>99
findの -name は正規表現ではありません。はい次
0101名無しさん@お腹いっぱい。2008/10/26(日) 22:54:30
>>99
-regex '[^0-9]*'
0102名無しさん@お腹いっぱい。2008/10/26(日) 22:59:07
>>99
-regex '.*/[^0-9]*'
0103名無しさん@お腹いっぱい。2008/10/26(日) 23:18:57
パス付きじゃなくてファイル名に対してはできないですか?
0104名無しさん@お腹いっぱい。2008/10/26(日) 23:25:04
>>103
-regex を使うなら、フルパスでしか指定できない。そういう仕様。
0105名無しさん@お腹いっぱい。2008/10/26(日) 23:27:37
>>99
-regex '.*/[^/0-9]*$'
0106名無しさん@お腹いっぱい。2008/10/26(日) 23:28:22
了解です
というか>>102でファイル名だけになってますね
ありがとうございました
0107名無しさん@お腹いっぱい。2008/10/27(月) 10:30:47
>>96
試したみたがそうはならんな。
まあzshスレの話題のような気もするが。

ちなみにv7 shもheirloom sh(OpenSolaris)も${1+"$@"}と書く必要がある。
(だから必ずそう書けといっているわけではない; 知っておいた方がいいというだけ)
0108名無しさん@お腹いっぱい。2008/10/27(月) 10:34:17
>>107
zshのバージョンは? 4.x または 5.x で症状が出る。
あと、ちゃんと setopt shwordsplit を設定したか?
0109名無しさん@お腹いっぱい。2008/10/27(月) 11:20:10
$ cat /tmp/foo ←>>96のコピペ
setopt shwordsplit
set 'hoge boke'
for i in ${1+"$@"}; do echo "$i"; done
for i in "$@"; do echo "$i"; done
$ zsh /tmp/foo
hoge boke
hoge boke
$ zsh --version
zsh 4.3.4 (i686-pc-linux-gnu)

zsh-newuser-installをquitしたので設定は何もない。
0110名無しさん@お腹いっぱい。2008/10/27(月) 11:42:08
>>109
zsh 4.2 だと >>96 の指摘通りになる。

zsh 4.3 だとバグフィックスされた?
01111092008/10/27(月) 11:55:03
setopt shwordsplitしようがしまいが結果は変わらんな
set 'hoge baka' ahoでも。
0112名無しさん@お腹いっぱい。2008/10/27(月) 12:09:18
>>111
だから、zsh-4.2 で試せと。

zsh-4.2 ならまだそう古いバージョンじゃないし、まだ使われてるし、
汎用性考えると "$@" と書くべきだな。
0113名無しさん@お腹いっぱい。2008/10/28(火) 22:37:42
初心者です。

ファイル名が、
年月日.種類.年日.場所.更新日時.拡張子
となっているファイルを扱っています。
年月日および年日(対応してます)は全部でおよそ400、場所は200〜300あり、
5種類分あるので、各種類約10万ファイル、全部で50万ファイルくらいあります。
この中から、年月日.種類.年日.場所.*.拡張子だけ同じで、
更新日時が違う複数のファイルを見つけ出し、古いファイルを消さねばなりません。

例を示すと
./2001.08.29/MOD15A2.A2001241.h15v03.005.2007052031007.hdf
./2001.09.06/MOD15A2.A2001249.h15v03.005.2007054111501.hdf
./2001.09.06/MOD15A2.A2001249.h15v03.005.2008278130254.hdf
./2001.09.14/MOD15A2.A2001257.h15v03.005.2007056161438.hdf
こんな感じです。この場合、2番目のファイルだけを消したいです。

ファイル名が完全一致の重複ではないだけに、どうしていいかわかりません。
できればお教えください。

ちなみにディレクトリ構造は、種類-年月日-ファイル、となっています。
0114名無しさん@お腹いっぱい。2008/10/28(火) 23:01:29
そのくらいの量だとシェルには荷が重くないか?
Perlあたりで書いた方がよくない?
0115名無しさん@お腹いっぱい。2008/10/28(火) 23:09:18
>>113
ヒントだけ。

全ファイルのリストをファイルに落す。(1行1ファイル名)
(ファイル作りたくなければパイプでも桶)

そのファイル(パイプ)を入力として、
対象フィールド限定で(更新日時フィールド以外で) uniq -d で重複行を出す。

その重複行それぞれについて、更新日時フィールドをワイルドカードにして
ファイル名リストをとる。この時、更新日時フィールドは結局辞書順に
並んでいるから、最後の行以外のファイルは古いファイルと判定できるから、
最後の行以外のファイルを rmする。
以上を、uniq -dが出力した各行について行なう。
0116名無しさん@お腹いっぱい。2008/10/29(水) 01:48:52
>>115
ありがとうございます!
考えてやってみます。
0117名無しさん@お腹いっぱい。2008/10/29(水) 03:16:42
シンボリックリンクの値を読む方法はreadlink以外にありますか?
readlink(1)がSUSv3に見当たらなくて驚いています
0118名無しさん@お腹いっぱい。2008/10/29(水) 08:26:48
ls -l
0119名無しさん@お腹いっぱい。2008/10/29(水) 10:27:19
>>113
ファイル名の桁が揃っていると仮定する。

sort -r d.txt | awk '{ k=substr($0, 0, 40); ++a[k]; print a[k], $0; }' | awk '$1 != 1 { print $2; }'
0120名無しさん@お腹いっぱい。2008/10/29(水) 11:01:42
>>117
perl -le 'print readlink($ARGV[0])' /hoge/file
0121名無しさん@お腹いっぱい。2008/11/02(日) 19:35:13
シェルスクリプトを実行するのに
sh hoge.sh
と打ち込んでいるのですが、これを
hoge.sh
と、ファイル名だけで実行できるようにするにはどうすればよいでしょうか?
0122名無しさん@お腹いっぱい。2008/11/02(日) 19:54:11
PATH
0123名無しさん@お腹いっぱい。2008/11/02(日) 19:56:56
chmod
0124名無しさん@お腹いっぱい。2008/11/02(日) 19:57:23
シェバン
0125名無しさん@お腹いっぱい。2008/11/02(日) 19:59:45
さて、誰が本物のエスパーか
0126名無しさん@お腹いっぱい。2008/11/02(日) 19:59:57
>>121
install -m 755 hoge.sh /bin
0127名無しさん@お腹いっぱい。2008/11/02(日) 20:00:54
あるフォルダ内にファイルが存在する場合、
フォルダ内のファイルを移動させたいのですが、

どういう条件式で記述するのが良いでしょうか?
0128名無しさん@お腹いっぱい。2008/11/02(日) 20:04:10
>>126
できました。ありがとう。

>>122 >>123
よくわかってませんが、それだとできませんでした。
0129名無しさん@お腹いっぱい。2008/11/02(日) 20:06:02
>>127
UNIXでフォルダと言えば、MHのフォルダのことだよな。
フォルダ内の全ファイルを移動するには、

refile all +newfolder
0130名無しさん@お腹いっぱい。2008/11/02(日) 20:20:30
>>129
MHフォルダ?分かりません。。。

現在だと
if[-d hoge] ; then
rm *** ***
fi
としてますが、エラーとなります。
何がまずいでしょうか?
0131名無しさん@お腹いっぱい。2008/11/02(日) 20:22:48
>>130
>>129は初心者を対象にしたいじわるだから気にするな。
0132名無しさん@お腹いっぱい。2008/11/02(日) 20:28:09
>>126も初心者を対象にしたいじわるじゃね?
0133名無しさん@お腹いっぱい。2008/11/02(日) 20:31:15
>>131
マジですか。。。

初心者をいじめないで下さい。
0134名無しさん@お腹いっぱい。2008/11/02(日) 21:14:30
てかPATHもchmodもわかんないでシェルいじるのは危険すぎねえ?
0135名無しさん@お腹いっぱい。2008/11/02(日) 21:37:35
>>130
とりあえず、エラーの原因は ifの後ろとか、[の後ろとか、]の前に
スペースを入れていないため。

ところで、ディレクトリが空じゃなかったら、
ファイルを移動したいのか?削除したいのか?
>>130>>127 で言ってることが矛盾してるのだが、、
0136名無しさん@お腹いっぱい。2008/11/02(日) 21:52:13
>>135
すみません、それではもう一度質問です。
フォルダ内のファイルが存在すれば
そのフォルダ内のファイルを他フォルダへ移動する(mv)という処理を
スクリプトで書くとどのような記述が良いでしょうか?
0137名無しさん@お腹いっぱい。2008/11/02(日) 22:08:45
>>136
多くの場合、

mv /hoge/* /hage 2> /dev/null
だけで十分なことが多い。
ファイルがなければ何も起きないし、そのエラーメッセージは捨ててるから。

ドットファイルにこだわるなら、
mv /hoge/* /hoge/.[^.]* /hoge/..?* /hage 2> /dev/null
かな。
0138名無しさん@お腹いっぱい。2008/11/02(日) 22:12:06
>>137
ありがとうございます、
確認してみます。。。
0139名無しさん@お腹いっぱい。2008/11/02(日) 22:24:00
できました!
ありがとうございました。

以前会社でやると
エラーがでましたが、
フォルダ内にファイルがなかったときは
mvコマンドでエラーにならないのですか?
0140名無しさん@お腹いっぱい。2008/11/02(日) 22:54:40
これが世に言う通常エラー出力のリダイヤルですか?
0141名無しさん@お腹いっぱい。2008/11/02(日) 23:02:18
二度出力してどうする
01421212008/11/03(月) 01:17:24
>>122
ありがとうございました。パス通すだけでよかったんですね、下記で動きました。
$ PATH=$PATH:/home/user_name
$ export PATH

>>123
パーミッションは大丈夫でした

>>124
それも大丈夫でした

>>126
ちょっとやりたいこととは違いました。

>>128
は私ではないです。
0143名無しさん@お腹いっぱい。2008/11/03(月) 04:04:06
問題の無いエラーなら無視するw
0144名無しさん@お腹いっぱい。2008/11/03(月) 14:21:47
大事なことなので二度出力しました
0145名無しさん@お腹いっぱい。2008/11/03(月) 21:44:25
関数を子シェルでも使えるように宣言できますか
0146名無しさん@お腹いっぱい。2008/11/03(月) 21:53:51
できません
0147名無しさん@お腹いっぱい。2008/11/03(月) 21:56:14
環境変数を子シェルに引き継がせない方法ありますか
0148名無しさん@お腹いっぱい。2008/11/03(月) 22:12:04
env -i
0149名無しさん@お腹いっぱい。2008/11/06(木) 22:00:56
vi
0150名無しさん@お腹いっぱい。2008/11/07(金) 20:18:50
たしかに↓でPCが逝ってしまいますね.rm -rf / よりはマシかもしれんが
:(){ :|:& };:
0151名無しさん@お腹いっぱい。2008/11/07(金) 21:18:29
>>150
やってみましたが、何も起きませんが、、

$ :(){ :|:& };:
: is a special built-in

あと、念のため、
$ ksh
$ :(){ :|:& };:
ksh: :: illegal function name
0152名無しさん@お腹いっぱい。2008/11/08(土) 00:20:40
唐突に「たしかに〜」って、なんだよ。
0153名無しさん@お腹いっぱい。2008/11/08(土) 00:46:00
bash じゃないと無理なのねorz

:() defines a function named : with no arguments.
{ :|:& } is what the function does.
:|: calls itself twice (with a pipe between the two), and the & at the end runs it in the background as a new process.
The ; finishes off that command, then the last : runs the function, starting the fork bomb
(as each run starts 2 new processes, each of which starts 2 new processes...).
0154名無しさん@お腹いっぱい。2008/11/08(土) 01:41:04
どっかのMLあたりのネタ?
0155名無しさん@お腹いっぱい。2008/11/08(土) 02:18:07
すげー試してぇ
0156名無しさん@お腹いっぱい。2008/11/08(土) 06:56:59
>>153
bashでも何も起きなかったぞ。

bash$ :(){ :|:& };:
bash: `:': not a valid identifier
0157名無しさん@お腹いっぱい。2008/11/08(土) 10:25:24
fork爆弾だろ
0158名無しさん@お腹いっぱい。2008/11/08(土) 10:33:30
シェル関数名に:を使って字面の面白さを狙ったものと思われるが、
高度なAAを見慣れたネラーには面白くもなんともない。
0159名無しさん@お腹いっぱい。2008/11/08(土) 14:27:17
2行目、AAとしてしか見れない人も可哀想だな
0160名無しさん@お腹いっぱい。2008/11/08(土) 18:19:09
いたずらコードを仕掛ける場合でも、
純正シェル対応にしておかないと効果半減する
ということですね、わかります
0161名無しさん@お腹いっぱい。2008/11/08(土) 23:55:15
大吟醸じゃダメですか。
0162名無しさん@お腹いっぱい。2008/11/09(日) 17:40:01
cygwinで(bashで)試してみたら、本当にPCが使えなくなったw
みんなはイタズラしないようにな
0163名無しさん@お腹いっぱい。2008/11/10(月) 13:45:55
ulimit -u 10 してからやれよ。
0164名無しさん@お腹いっぱい。2008/11/11(火) 01:28:28
オプションを処理したあと、引数が残っていたらファイルとして、
そうでなければ標準入力を処理したいのですが、
if [ $# -gt 0 ]; then
fuga "$@" | hoge
else
fuga | hoge
fi
みたいにやらずにスマートにするにはどう書いたらよいのでしょうか。
上の例のfuga自体は引数のファイルも標準入力もOKとします。
0165名無しさん@お腹いっぱい。2008/11/11(火) 07:44:32
>>164
そのまま、

fuga "$@"

だけでOK。
$# が 0 なら、"$@" は消えてなくなってくれる。

( ${1+"$@"} と書く流儀もあるが、 >>96 の指摘の通り zshにバグがあるので、
"$@" の方がお勧め)
0166名無しさん@お腹いっぱい。2008/11/11(火) 08:36:49
> zshにバグがあるので

古いのだけだろ。
■ このスレッドは過去ログ倉庫に格納されています