トップページ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/
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にバグがあるので

古いのだけだろ。
0167名無しさん@お腹いっぱい。2008/11/11(火) 11:08:38
>>166
純正シェルスクリプトは古いシェルをも想定しなければならない。
しかも、zshの件はそんなに古くないバージョンの話だし。
0168名無しさん@お腹いっぱい。2008/11/11(火) 11:19:03
バグ持ちにあわせる必要はない。
0169名無しさん@お腹いっぱい。2008/11/11(火) 11:23:13
動いてなんぼ。原因がバグであろうと、普及していればそれが仕様。
存在するものに合わせなければならないのは当然。
0170名無しさん@お腹いっぱい。2008/11/11(火) 11:26:14
>>169
まるでMicroSoftのようだ
0171名無しさん@お腹いっぱい。2008/11/11(火) 11:26:50
これからバグだらけのシェル開発するよ
0172名無しさん@お腹いっぱい。2008/11/11(火) 11:32:42
バグのない純米シェルはありませんか?
0173名無しさん@お腹いっぱい。2008/11/11(火) 14:30:07
純正シェルスクリプトなら zsh で動かすことを考慮する必要ないのでは
0174名無しさん@お腹いっぱい。2008/11/11(火) 14:34:45
/bin/sh -> zsh と symlinkされたOSがあったりする。
0175名無しさん@お腹いっぱい。2008/11/11(火) 20:46:41
zshみたいなうんこシェルとそれが/bin/shになったOSよりSolaris優先。
0176名無しさん@お腹いっぱい。2008/11/11(火) 21:04:36
bashよりはマシだろ
スクリプトの速度的に
0177名無しさん@お腹いっぱい。2008/11/11(火) 21:38:18
Solarisもbashになったけどな。
0178名無しさん@お腹いっぱい。2008/11/12(水) 04:25:36
それほど大掛かりなことをする訳ではなく、
シェルスクリプトから軽くXML内の値を参照したり、
書き換えたりしたいんだが…

XMLStarletなるツールを使えば、ほぼ満足の行く内容で目的は
達せられるんだけど、なんかもっとメジャーな、デファクト
スタンダードなツールってありますかね?
なんか、検索しても、イマイチ普及しているように感じられないん
ですけど。
0179名無しさん@お腹いっぱい。2008/11/12(水) 04:39:33
${1+"$@"}みたいにする意味をネットで調べたくても、
うまく検索できないんだよなぁ。
シェルスクリプト本でも読みに本屋行ったほうが早いかも。
0180名無しさん@お腹いっぱい。2008/11/12(水) 09:58:27
>>178
xmlgawkてのはどう?
デファクトじゃあないだろうけど
0181名無しさん@お腹いっぱい。2008/11/12(水) 15:41:51
>>179
$1が定義されてたら"$@"で置換。
純正シェルは$1が未定義の時に"$@"が空文字列に置換される。
0182名無しさん@お腹いっぱい。2008/11/12(水) 16:30:07
意味を知りたいんじゃなくて
意味の知り方を知りたいだろう。
0183名無しさん@お腹いっぱい。2008/11/12(水) 19:41:43
いや、意味というか仕組みを知りたくて、
そのために調べようとしても分からなかったので、
知り方も知りたかったです。(で、分からなくてボヤいた。)

で、Parameter Expansionを利用した細工とも違うみたいで、
やはり仕組みはよく分からなかったです。
# ${parameter:+word} はmanにあるけど、
# 今回の場合 ":" は無いので。
0184名無しさん@お腹いっぱい。2008/11/12(水) 20:08:48
: がない場合の動作もmanに明記してあるよ。
(FreeBSDのshとbashで確認)
0185名無しさん@お腹いっぱい。2008/11/12(水) 20:27:09
見当たらん・・・
0186名無しさん@お腹いっぱい。2008/11/12(水) 20:45:48
:+ とか :- とかの書式について書いてある近辺に、
: の有無で動作がどう変わるか書いてある。
${parameter+word} 自体で独立した項目にはなってるわけではない。
0187名無しさん@お腹いっぱい。2008/11/12(水) 20:50:50
あぁ、「colon」か。「:」で探してた。
ども。
0188名無しさん@お腹いっぱい。2008/11/12(水) 21:56:35
>>181
嘘書くな。

純正シェルでも、$1がセットされていなくても、
"$@" は空文字列にはならない。(何もない状態になる)

よって、${1+"$@"} にする必要無し。
0189名無しさん@お腹いっぱい。2008/11/12(水) 22:11:11
v7shなら"$@"は空文字列になる。
具体的なシェルを書かない純正論は空論。
0190名無しさん@お腹いっぱい。2008/11/12(水) 22:13:51
${1+"$@"} にする(ことがあった)のは空文字列対策じゃないわけだが、、
0191名無しさん@お腹いっぱい。2008/11/12(水) 22:37:39
以前くだ質で読んだ気が
0192名無しさん@お腹いっぱい。2008/11/12(水) 23:48:27
v7$ cat foo
c=0
for i in "$@"; do
echo "$c: $i"
c=`expr $c + 1`
done
v7$ ./foo "1 2 3" 4 5
0: 1 2 3
1: 4
2: 5
v7$ ./foo
0:
v7$ cat bar
c=0
for i in ${1+"$@"}; do
echo "$c: $i"
c=`expr $c + 1`
done
v7$ ./bar "1 2 3" 4 5
0: 1 2 3
1: 4
2: 5
v7$ ./bar
v7$
0193名無しさん@お腹いっぱい。2008/11/12(水) 23:58:39
そんなおおげさにしなくても、echo foo "$@" bar で確認できるでしょ。
0194名無しさん@お腹いっぱい。2008/11/13(木) 08:23:20
>>192
>>96
0195名無しさん@お腹いっぱい。2008/11/13(木) 08:51:45
>>194
>>168
0196名無しさん@お腹いっぱい。2008/11/13(木) 19:11:37
梶谷秀
0197名無しさん@お腹いっぱい。2008/11/13(木) 22:19:24
バグがあっても周囲がそれに合わせるべきと見なされる存在、
それが純正シェル(笑)
0198名無しさん@お腹いっぱい。2008/11/14(金) 17:42:12
>>120-135あたりを読むと 腹が痛いくらい笑えるね。
0199名無しさん@お腹いっぱい。2008/11/14(金) 18:34:57
誰もが通ったor通ってる道だ
0200名無しさん@お腹いっぱい。2008/11/14(金) 18:40:51
いや、別にそんなに面白くはない。
0201名無しさん@お腹いっぱい。2008/11/14(金) 18:45:17
誰もが通る道だが

ネット検索や入門書などで調べる人
頭から調べようとしない人
検索しても目的のものを見つけられない人

いろいろだわな。
0202名無しさん@お腹いっぱい。2008/11/14(金) 21:00:46
俺はそんな道は通ってないが、もちろんシェルスクリプトに関してはエキスパート。
0203名無しさん@お腹いっぱい。2008/11/14(金) 23:12:52
Xパート?
0204名無しさん@お腹いっぱい。2008/11/14(金) 23:16:29
標準出力と標準エラーってなんで分かれてるんですか
0205名無しさん@お腹いっぱい。2008/11/15(土) 00:12:11
シャバン
0206名無しさん@お腹いっぱい。2008/11/15(土) 00:14:18
>>204
例えばトイレには男子トイレと女子トイレがある。
本質的にはそれと一緒。
0207名無しさん@お腹いっぱい。2008/11/15(土) 01:08:31
大昔は、標準出力用のディスプレイと標準エラー用のディスプレイに分かれてた名残と聞いたような聞いてないような
0208名無しさん@お腹いっぱい。2008/11/15(土) 04:54:16
>>204
$ foo file1 file2 file3 | wc
file2: not found
10 23 445

"file2: not found"までwcしても意味がない。
エラー情報だけ別に扱うことができる。

GNU系のコマンドがusageをstdoutに出すのが困る。
オプション間違ってた時に。

0209名無しさん@お腹いっぱい。2008/11/15(土) 11:02:02
COMMAND.COMではstderrがリダイレクトできないので、usageをstderrに吐く
ツールはリダイレクトしてチートシートを作れなくて面倒だったなぁ
0210名無しさん@お腹いっぱい。2008/11/15(土) 11:21:21
そしてstderrを自由にリダイレクトできないcsh批判へ
0211名無しさん@お腹いっぱい。2008/11/15(土) 12:47:14
できればいいじゃん
0212名無しさん@お腹いっぱい。2008/11/15(土) 13:08:04
csh ? なんですだそりゃ?
0213名無しさん@お腹いっぱい。2008/11/15(土) 13:18:37
標準エラー入力を新設するべきだ。

hoge | less

ってやった時、lessは標準入力からパイプを読むが、
この時、less自体の操作のためにキーボードを読む必要があるが、
/dev/ttyを直接読まずに、「標準エラー入力」を読んだ方が美しいし応用も効く。
0214名無しさん@お腹いっぱい。2008/11/15(土) 13:50:32
キーボードを読む必要があるのなら、キーボードを読むべきじゃないの?

もしかして、エラー出力≒コンソール出力、って思ってるなら、
あんまりそういうことはないとだけ言っておく
0215名無しさん@お腹いっぱい。2008/11/15(土) 14:00:25
バックグラウンド起動したときに出るpidとかのメッセージは何出力ですか?
0216名無しさん@お腹いっぱい。2008/11/15(土) 14:14:26
>>215
$ : & > /dev/null
とか
$ : & 2> /dev/null
とかやってみて確かめればいいだろ。
0217名無しさん@お腹いっぱい。2008/11/15(土) 14:33:04
>>216
それだと : の stdout や stderr が /dev/null になるんじゃない?

exec 2>/dev/null
: &

とかしないと
0218名無しさん@お腹いっぱい。2008/11/15(土) 14:48:01
>>216
[root@migii ~]# : & > /dev/null
[1] 2420
[root@migii ~]# : & 2> /dev/null
[2] 2421
[1] Done :
[root@migii ~]#
0219名無しさん@お腹いっぱい。2008/11/15(土) 16:09:56
>>214
無端末状態で lessを起動しようとすると、not a tty って言って拒否されるけど、
標準エラー入力があれば、/dev/tty をじか読みしないから起動できると言う
メリットがある。

「キーボードを読む必要がある」のじゃなく、
「最終的にキーボードにつながっているはずのパイプとかソケットとか」
を読むための「標準エラー入力」
0220名無しさん@お腹いっぱい。2008/11/15(土) 16:13:48
AEGISにはあったな。
0221名無しさん@お腹いっぱい。2008/11/15(土) 16:17:52
>>214 は知ったかだな。
コンソールとTTYもまた別物だし。
何が、"言っておく" だ。
0222名無しさん@お腹いっぱい。2008/11/15(土) 16:23:29
>>219
controlling terminalを知らない人?
0223名無しさん@お腹いっぱい。2008/11/15(土) 16:26:11
>>222
もちろん知ってるよ。
知った上で言ってるよ。

制御端末はどちらかと言うとプロセス管理上の問題(シグナルとか)だから、
今の話とはちょっとずれてる。
制御端末がなくても標準エラー入力でlessは動作可能。
0224名無しさん@お腹いっぱい。2008/11/15(土) 16:31:07
>>222 は、controlling terminal と pseudo terminal を混同してる悪寒。

pseudo terminalだったとしてもlessにとって必須というわけじゃないしなぁ。
パイプのような dumb入力でも動作可能。
0225名無しさん@お腹いっぱい。2008/11/15(土) 16:36:03
どうでもいいが、仮名にしても標準エラー入力でなく
もうちょっとマシな命名してくれよ。
0226名無しさん@お腹いっぱい。2008/11/15(土) 16:41:05
標準エラー出力に対するものだから標準エラー入力と言ってるだけで、
今から命名するなら「標準診断入力/出力」とかだろな。
0227名無しさん@お腹いっぱい。2008/11/15(土) 16:45:41
>>219
何がやりたいのか不明。
0228名無しさん@お腹いっぱい。2008/11/15(土) 18:24:10
ファイルのタイムスタンプを、引数指定の秒数(または分や時間)だけ
相対的に進める(または遅らせる)ようなシェルってどう書きますか?

touchやlsを駆使すればできそうですが、なるべく奇麗な方法でよろしこ。
0229名無しさん@お腹いっぱい。2008/11/15(土) 18:30:10
touch -A
0230名無しさん@お腹いっぱい。2008/11/15(土) 18:50:52
>>229
それ、どこのtouchだよw
0231名無しさん@お腹いっぱい。2008/11/15(土) 19:27:47
>>228
GNU dateで、
touch --date=`date --date='1 year ago'` filename
0232名無しさん@お腹いっぱい。2008/11/15(土) 19:35:12
>>231
それだと、「現在時刻」からの相対時刻になっちゃうじゃん
質問は元のファイルの時刻からの相対
0233名無しさん@お腹いっぱい。2008/11/15(土) 19:48:57
結局>>215はなんなの?
0234名無しさん@お腹いっぱい。2008/11/15(土) 19:53:03
>>233
シェル自体の標準エラー出力だよ。
だから、コマンド起動時に単純に 2> /dev/null とかでリダイレクトできないから
わかりにくいだけ。
0235名無しさん@お腹いっぱい。2008/11/15(土) 20:00:00
補足すると、>>217 の言うように、exec 2>/dev/null すれば確かめられる。
ただし、exec 2>/dev/null した瞬間からシェルのプロンプトも
表示されなくなるので、操作困難になる。
ついでに、シェルのプロンプトも標準エラー出力というわけだ。
0236名無しさん@お腹いっぱい。2008/11/15(土) 20:00:58

$ find . -maxdepth 1 -name '*foo*' | xargs mv dir/

のようにして '*foo*' にマッチしたファイルを dir/ 下に移動したいんですが、パイプ
からの出力がdir/ の後ろに位置してしまい、
$ mv dir/ $files
のように解釈されてしまう為、うまくいきません。

$ mv $files dir/
のように解釈させるにはどうすればいいですか?
0237名無しさん@お腹いっぱい。2008/11/15(土) 20:05:16
-i {} dir
0238名無しさん@お腹いっぱい。2008/11/15(土) 20:12:26
>>237
できました。ありがとうございました。
0239名無しさん@お腹いっぱい。2008/11/15(土) 20:14:08
>>238
どこの xargs つかってるの?
0240名無しさん@お腹いっぱい。2008/11/15(土) 20:22:29
-exec涙目
0241名無しさん@お腹いっぱい。2008/11/15(土) 20:44:58
xargsはobsolete

-exec mv {} dir +
0242名無しさん@お腹いっぱい。2008/11/15(土) 20:51:42
-exec sh -c 'mv "$@" OOO' dummy {} +
0243名無しさん@お腹いっぱい。2008/11/15(土) 22:24:25
>>234 >>235
ありがと
結局これを普通にリダイレクトは無理ということ?
0244名無しさん@お腹いっぱい。2008/11/15(土) 22:28:44
>>243
もし、& 付けた後の pid表示が邪魔だから消したいという話なら、

$ (command &) 2> /dev/null

で消せるよ。
実は、

$ (command &)

だけでも消えるんだけどね。
0245名無しさん@お腹いっぱい。2008/11/16(日) 01:59:13
今使ってるシェルを知るには$SHELLを見ればいいって言われたんだけど
bashからcshに変えても値が/bin/bashのままなんだけど・・
0246名無しさん@お腹いっぱい。2008/11/16(日) 09:37:17
>>245
cshはバカだから$shell。
% set
% setenv
すれば、一覧が見れる。
0247名無しさん@お腹いっぱい。2008/11/16(日) 09:54:28
>>246
thx
0248名無しさん@お腹いっぱい。2008/11/16(日) 10:17:00
>>228 の解答まだぁ?
0249名無しさん@お腹いっぱい。2008/11/16(日) 10:20:41
kshって色づけ出来ないの?
0250名無しさん@お腹いっぱい。2008/11/16(日) 10:41:22
cshは、もはや継続行のコメントアウト部分のみしか羨ましくない
0251名無しさん@お腹いっぱい。2008/11/16(日) 22:08:14
>>228,248
つ touch -d @`stat -c %Y hogehoge | xargs expr -3600 +` hogehoge

$ touch hogehoge
$ ls -l hogehoge
0 -rw-r--r-- 1 xxx users 0 Nov 16 22:05 hogehoge
$ sudo touch -d @`stat -c %Y hogehoge | xargs expr -3600 +` hogehoge
$ ls -l hogehoge
0 -rw-r--r-- 1 xxx users 0 Nov 16 21:05 hogehoge

ただしGNU coreutils使用。
0252名無しさん@お腹いっぱい。2008/11/16(日) 22:20:22
>>251
GNUありでいいならもっと簡単にできるだろ。
0253名無しさん@お腹いっぱい。2008/11/16(日) 23:23:50
うぉ -r ってのがあるのね・・・アリガトン>252
0254名無しさん@お腹いっぱい。2008/11/16(日) 23:51:58
なぜsudo?
0255名無しさん@お腹いっぱい。2008/11/17(月) 00:25:30
あれ、ホントだ。たぶんchownと脳と指が取り違えて打ってた。
この癖はリアルで危険だから直さんといかんな・・・
0256名無しさん@お腹いっぱい。2008/11/17(月) 00:35:33
キ違いに刃物
0257名無しさん@お腹いっぱい。2008/11/17(月) 07:26:21
>>218>>251
先週末はルート祭だったのね。
0258名無しさん@お腹いっぱい。2008/11/17(月) 22:10:51
教えてください。

#!/bin/sh

TestFunc()
{
echo $1 # ここでは ls -l "aaa bbb"
$1 # ここでは ls -l aaa bbb と処理されてしまう
}

TestFunc "ls -l \"aaa bbb\""

Bシェルで関数に引数を与え、コマンドを実行することを
考えています。
コマンドの引数にスペースつきのものを含む場合、ダブルコーテーションで
くくっていても、実行される場合は外されてしまうようです。

そもそもechoで出した場合は正しく表示されるのに、実行した場合
ダブルコーテーションが外れてしまう理由も良く分からないのですが、
どのようにすれば正しく実行できるのでしょうか?

TestFuncc()
{
$1 $2 "$3"
}

TestFunc "ls" "-l" "aaa bbb"

とすれば良いのは分かるのですが、コマンドの引数が変わった場合に対応できないので
汎用性が落ちますし、出来れば一つの文字列で関数に渡したいのです
0259名無しさん@お腹いっぱい。2008/11/17(月) 22:16:57
>>258
TestFunc()
{
echo "$@"
"$@"
}
0260名無しさん@お腹いっぱい。2008/11/17(月) 22:30:13
>>259
TestFunc()
{
echo "$@"
eval "$@"
}
だろ?
■ このスレッドは過去ログ倉庫に格納されています