トップページ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 でトレースしましょう。
0304名無しさん@お腹いっぱい。2006/11/08(水) 01:13:34
ファイル実行しようとして実行できないときに表示されるエラーなど(まぁ、エラー表示全般)を
表示しない方法ってありますか?
/dev/null 
は試してみましたが、無理でした。
03053042006/11/08(水) 01:14:06
いい忘れたけど、Bashでう
0306名無しさん@お腹いっぱい。2006/11/08(水) 06:46:12
>>304
2>/dev/nullでどう
0307名無しさん@お腹いっぱい。2006/11/08(水) 08:01:42
エラー表示って、/dev/null にどうやって投げるんだ?
無理だろ
0308名無しさん@お腹いっぱい。2006/11/08(水) 08:39:00
>>307
だから、 2> /dev/null でエラー表示消せるんだってすでに >>306 が言ってるだろ。
0309名無しさん@お腹いっぱい。2006/11/08(水) 09:15:34
>ファイル実行しようとして実行できないときに表示されるエラーなど

は実行しようとしているコマンドではなくシェルが出してるので、
/dev/null にリダイレクトしても消せない。

つーわけで、シェルの出力をリダイレクトしてやればいい。

sh -c 'hoge fuga' 2>/dev/null
0310名無しさん@お腹いっぱい。2006/11/08(水) 09:24:40
>>309
欲嫁。

>>305 で bashだと言ってる。bashなら消せる。

bash$ hoge
hoge: command not found

bash$ hoge 2> /dev/null
bash$
0311名無しさん@お腹いっぱい。2006/11/08(水) 09:30:42
>>299
execすれば同じプロセスだろ。どの程度意味があるかは俺にもよくわからんが(笑)
0312名無しさん@お腹いっぱい。2006/11/08(水) 09:39:50
>>311
execしても、PIDが変わらないだけで別プロセス扱いになるので、
exec前にバックグラウンドで起動したプロセスを wait で待つことはできないよ。
0313名無しさん@お腹いっぱい。2006/11/08(水) 10:14:53
>>312
ありゃーwaitpidはできたよなと思ってよく調べたらshellのwaitコマンドはシステムコール
呼ぶ前にチェックしてるのか(bash調べ)。知らんかった...
0314名無しさん@お腹いっぱい。2006/11/08(水) 17:15:17
touch で使える書式で、あるファイルの更新時刻を取得したいのですが、
もっとシンプルor可搬性のある方法はあるでしょうか?
hoge=`stat -c %y FILENAME|sed -e 's/..\..*//' -e 's/-//g' -e 's/://g
' -e 's/ //g'`
touch -t "$hoge" FILENAME2
0315名無しさん@お腹いっぱい。2006/11/08(水) 17:20:32
-rオプションが使えるtouchならそれを使うのがいいと思われる。

-r, --reference=FILE
use this file's times instead of current time
03163142006/11/08(水) 18:05:29
ファイルを編集したあと、その更新時刻を編集前に戻したい
というのが動機なので、それだとだめなのです。
0317名無しさん@お腹いっぱい。2006/11/08(水) 18:31:03
別のファイルを作って、更新時刻だけコピーして、
編集してから更新時刻をコピーし戻せば?
0318名無しさん@お腹いっぱい。2006/11/09(木) 00:01:36
動機が不純
0319名無しさん@お腹いっぱい。2006/11/09(木) 00:06:46
>>316
それだって touch -r で行けるじゃん。

$ touch -r hoge temp
$ vi hoge
$ touch -r temp hoge
$ rm temp

↑で、hogeを編集したあと、もとのタイムスタンプに戻せる。
0320名無しさん@お腹いっぱい。2006/11/09(木) 03:18:35
シェルスクリプトでC言語のscanfみたいなことやるためのコマンドは何でしょうか?
0321名無しさん@お腹いっぱい。2006/11/09(木) 09:36:02
touchもいろいろあるのでよくわからんが、gnuなら時刻指定に@<epochからの秒数>が
使えるはずなので、

hoge=`stat -c %Y FILENAME`

でとりだして

touch -d @$hoge FILENAME

とするのがシンプルじゃないか。
0322名無しさん@お腹いっぱい。2006/11/09(木) 09:42:34
>>320
強いて言えば read が近いと思うが、シェルスクリプトとCじゃ考え方が違う。
scanfと同じものを探すより頭を切り替えたほうがいいと思われる。
0323名無しさん@お腹いっぱい。2006/11/09(木) 14:31:53
外部コマンドとのやりとりがシェルスクリプトなみに簡潔で
変数や構文の取り扱いがperlとかruby程度に洗練された
スクリプト言語って何かない?
zshとかbashの専用機能使えばそこそこましなのかな?
0324名無しさん@お腹いっぱい。2006/11/09(木) 16:17:52
>>323
scsh
0325名無しさん@お腹いっぱい。2006/11/09(木) 16:28:29
>>323
python
0326名無しさん@お腹いっぱい。2006/11/09(木) 16:39:16
Perl とか Ruby でいいじゃん。
0327名無しさん@お腹いっぱい。2006/11/09(木) 18:10:24
continuation がまともに使えない ruby は屑
使えないんだったら実装するなと小一時…
0328名無しさん@お腹いっぱい。2006/11/09(木) 18:12:56
ここはそういうスレではありません。
0329名無しさん@お腹いっぱい。2006/11/10(金) 02:15:32
シェルスクリプトの変数について質問です。

以下のように値が格納された3つの変数があるとします。
hoge1=10
hoge2=20
hoge3=30

これを、
for i in `jot 3`
do
echo $hoge$i
done

のようなイメージで、変数名を展開し、
それから変数の値を展開したい場合の方法は
シェルスクリプトではどのように行うでしょうか?
0330名無しさん@お腹いっぱい。2006/11/10(金) 02:40:09
/etc/rc* 等を見れば例があると思うけど。キーワードはeval。

あなたのしたいことを実現するためには、
echo $hoge1
のような文字列をevalさせればいい。ということは、evalに与えるための
echo $hoge1 のような文字列が作れればいい。

そのまま$hogeとなる文字列に、1を作る変数展開をくっつけて
echo '$hoge'$i
と書くとそれは作れる。

ということは、答えは
eval echo '$hoge'$i


$ hoge1=foo
$ hoge2=bar
$ hoge3=baz
$ for i in `jot 3`; do eval echo '$hoge'$i; done
foo
bar
baz
0331名無しさん@お腹いっぱい。2006/11/10(金) 06:12:56
安易にevalをつかうな。
配列にした方がいい。
0332名無しさん@お腹いっぱい。2006/11/10(金) 07:05:17
配列はbash依存。evalを使うのが普通。
0333名無しさん@お腹いっぱい。2006/11/10(金) 08:19:36
配列はksh起源ニダ。
0334名無しさん@お腹いっぱい。2006/11/10(金) 18:53:54
evalを安易に使うなというのは同意。
でも、>>329の場合は使ってよいケース。

set使ってもいいけど。
0335名無しさん@お腹いっぱい。2006/11/10(金) 21:52:15
bashだけだろ?
eval使えるの
0336名無しさん@お腹いっぱい。2006/11/10(金) 22:23:34
>>335
釣りにも最低限の知識が必要。
0337>>3292006/11/11(土) 02:47:00
みなさま、ありがとうございます!!

evalは初めて知りました。

早速試してみますー
0338名無しさん@お腹いっぱい。2006/11/11(土) 09:35:47
>>335
lisp1.5でも使えるぞ。
0339名無しさん@お腹いっぱい。2006/11/11(土) 11:27:45
evalが使えるからってえばるなよ。
0340名無しさん@お腹いっぱい。2006/11/11(土) 12:21:48
>>339
全米が失笑した
0341名無しさん@お腹いっぱい。2006/11/11(土) 14:26:06
bashの良いところはこれだな。

echo ls |bash
0342名無しさん@お腹いっぱい。2006/11/12(日) 02:57:55
どのシェルでも出来るだろ?
0343名無しさん@お腹いっぱい。2006/11/13(月) 21:35:17
シェルスクリプトの中で vi を起動しているのですが、
そのスクリプトの標準出力をリダイレクトしてしまうと
nvi の場合は
ex/vi: Vi's standard input and output must be a terminal
と出て終了し、vim の場合は
Vim: Warning: Output is not to a terminal
と出て画面が表示されなくなってしまいます。
リダイレクトしてても普通に vi を操作できるようにする
ことはできないのでしょうか?
0344名無しさん@お腹いっぱい。2006/11/13(月) 21:42:52
>>343
できない。そういう場合は ed とか sed とかを使う。
0345名無しさん@お腹いっぱい。2006/11/13(月) 23:03:31
>>344
無理ですか。ありがとうございます。
0346名無しさん@お腹いっぱい。2006/11/13(月) 23:13:50
>>343
そのスクリプトの目的が中でviに適当なコマンドを与えて自動的に編集させたいということなら
expectでも使わないとだめだろうが、スクリプトで前処理や後処理はするけどそこから起動した
viは普通にインタラクティブに操作したいというのであれば、vi > /dev/tty とかすればいいと
思われる。どっちを意図してるのかはわからんわけだが。


0347名無しさん@お腹いっぱい。2006/11/14(火) 05:11:39
>>346前半
ed
0348名無しさん@お腹いっぱい。2006/11/14(火) 20:11:42
>>346
前者です。>/dev/tty でできました。
ありがとうございます。
0349名無しさん@お腹いっぱい。2006/11/14(火) 20:13:27
あ、後者だった。
0350名無しさん@お腹いっぱい。2006/11/16(木) 22:18:34
ls|grep -v hoge|xargs rm -i
ではまってしまった。
rm -i `ls|grep -v`
でごまかした。
0351名無しさん@お腹いっぱい。2006/11/17(金) 10:38:58
カレントディレクトリにある、拡張子のないファイル「だけ」を消すために、
rm *
を実行したら、すべてのファイルが消えてしまいました。(rm *.*じゃないのに)
これって、シェルのバグじゃないでしょうか?

あと、消してしまったファイルを復活する方法を教えてください。
0352名無しさん@お腹いっぱい。2006/11/17(金) 10:46:01
仕様です。

>> あと、消してしまったファイルを復活する方法を教えてください。

バックアップから戻せばOK
0353名無しさん@お腹いっぱい。2006/11/17(金) 11:51:49
そもそも UNIX では拡張子に大した意味ないんだよ。(一部アプリ以外)
普通は「拡張子」なんて言わず単に「suffix」って言うし。
0354名無しさん@お腹いっぱい。2006/11/17(金) 11:53:13
そうか、Makefileとかは例外の「一部アプリ」だったのか、、
0355名無しさん@お腹いっぱい。2006/11/17(金) 12:06:03
>>354
突っ込み処が不明なんだが…
0356名無しさん@お腹いっぱい。2006/11/17(金) 19:37:09
>>351
拡張子の無いファイル名に一致するシェル表現かー
.loginのようにドットで始まるファイルは対象なのか対象外なのかむずかしい
0357名無しさん@お腹いっぱい。2006/11/17(金) 19:43:00
>>356
.loginとかは対象外だろ(つーか、cshの例出すなよ)

hoge.c hoge hage.c hage

↑がある時、hogeとhageだけ消したいということだろ。
そう言えば、拡張子なしのファイルに一致するワイルドカードって
シェルにはないんだな。DOSだと「*」で桶なんだけど。
0358名無しさん@お腹いっぱい。2006/11/17(金) 21:19:06
zshなら*~*.*
0359名無しさん@お腹いっぱい。2006/11/17(金) 21:22:54
>>357
find . -maxdepth 1 \! -name \*.\* -delete
0360名無しさん@お腹いっぱい。2006/11/17(金) 23:21:23
分からないので教えてください。
シェルの中で、あるシェルを呼び出すことを考えています。
2つのシェルはカレントであれば、実行可能なんですが、
パスを変えて実行するにはどうすればよいでしょうか?

例:
# pwd
/home/hoge1

# ls ../hoge2
A.sh Z.sh

# cat ../hoge2/A.sh
#! /bin/sh

date
./Z.sh

# cat ../hoge2/Z.sh
#! /bin/sh

echo "Call OK!"

# ../A.sh
2006年 11月 17日 金曜日 23:15:46 JST
../A.sh: line 4: ./Z.sh: そのようなファイルやディレクトリはありません
0361名無しさん@お腹いっぱい。2006/11/18(土) 00:21:05
A.sh

#!/bin/sh
pwd
0362名無しさん@お腹いっぱい。2006/11/18(土) 00:44:52
>>360
死ねよ
0363名無しさん@お腹いっぱい。2006/11/18(土) 01:50:24
拡張子の無いファイルを消すというか、「.」が無いファイルを消せば。
rm `ls * | fgrep -v .`
ファイル名にスペースがあると不十分かもしれん。
0364名無しさん@お腹いっぱい。2006/11/18(土) 05:10:01
>>361
ありがたいのですが、意味がわからないです。
0365名無しさん@お腹いっぱい。2006/11/18(土) 07:22:59
IFSで区切られた必要なファイルのリストがあって、そのリストには載ってないが
実在する余計なファイルを削除するにはどうしたらいいでしょうか?

#必要なファイル100個とか
files="a.txt 1.html hoge.jpg"

#実在するファイル100+1個以上
e_files="1.html a.txt fuga.gif hoge.jpg"
0366名無しさん@お腹いっぱい。2006/11/18(土) 09:42:33
>>363
ディレクトリというのがUNIXにはあるんだけども。

>>365
試してないけどこんな感じで
contains () {
 local x="$1"; shift
 for i; do
   [ "x$i" = "x$x" ] && return 0
 done
 return 1
}
for f in $e_files; do
 contains "$f" $files || echo "rm $f"
done
0367名無しさん@お腹いっぱい。2006/11/18(土) 11:00:01
>>360
シェルってゆうな。クズ
0368名無しさん@お腹いっぱい。2006/11/18(土) 12:01:09
>>367
なんと呼ぶの?
0369名無しさん@お腹いっぱい。2006/11/18(土) 12:07:06
>>368
>>1嫁。クズ。
0370名無しさん@お腹いっぱい。2006/11/18(土) 12:17:17
>>366 ディレクトリというのがUNIXにはあるんだけども。

ls ではディレクトリは出力されませんがな。
alias 付けてなければ。
0371名無しさん@お腹いっぱい。2006/11/18(土) 12:26:34
>>370
こんなんなるんだけど。

$ touch fff
$ mkdir ddd
$ touch ddd/fff
$ /bin/ls *
fff

ddd:
fff
$ rm `/bin/ls * | fgrep -v .`
rm: cannot remove `ddd:': No such file or directory
rm: cannot remove `fff': No such file or directory
$
0372名無しさん@お腹いっぱい。2006/11/18(土) 12:42:51
>>369 大変失礼しました。で、そうのようなコメントがあるのであれば
どうすればよいのでしょうか。
0373名無しさん@お腹いっぱい。2006/11/18(土) 12:54:17
>>350
ちょっとメッセージが違うけど、これでいけることがわかった。
ls|grep -v hoge|xargs -p -n 1 rm
03743702006/11/18(土) 12:58:27
>>371
ああ、そういうことか。
>>370は変なツッコミだった。スマソ。

ls * だとディレクトリの下のファイルがでちゃうから ls -d * だな。

さらに、はじめからディレクトリを除外するとしたら

for x in `ls -d * | fgrep -v .`; do
if [ ! -d $x ]; then
echo $f
fi
done

かな

ls -d * | fgrep -v . | xargs file | fgrep -v 'directory' | awk -F: '{print $1}' | xargs rm

ってのもあるけど
0375名無しさん@お腹いっぱい。2006/11/18(土) 13:19:33
>>365

安直なやり方として、

rm `fgrep -v -f files e_files` # 最初は rm じゃなくechoで試す

ってのがある。
ただし、files と e_files は 1行1ファイルでリストされたものとして。
さらに、files の中に "hoge.jpg" があると、e_files に中にある "hogehoge.jpg" は
削除されないけど。
0376名無しさん@お腹いっぱい。2006/11/18(土) 15:54:42
WindowsPowershellについてご意見ください。
よろしくお願いします。

http://www.forest.impress.co.jp/article/2006/11/15/windowspowershell.html
http://support.microsoft.com/kb/926140
0377名無しさん@お腹いっぱい。2006/11/18(土) 17:30:16
>>360
シェルとシェルスクリプトは別物だぞw

Z.shのところ絶対パスでやれ。
それだけだ。
0378名無しさん@お腹いっぱい。2006/11/18(土) 17:32:26
('з')
0379名無しさん@お腹いっぱい。2006/11/18(土) 17:33:39
for文について質問です。
内容はfor文のループ変数に配列を代入することは可能なのか?ということです。

for STARTEND in "(1 100)" "(101 200)";do
START=${STARTEND[0]}
END=${STARTEND[1]}
echo -e "&Avestart\nAve_start=${START}/\n&Aveend\nAve_end=${END}/" > namelist/namelist${START}to${END}
done
なるスクリプトを書いてみたのですが、
./speccalc.sh: line 15:(1 100): missing `)' (error token is "100)")
なるエラーが出ます。


もしbashで配列を配列のまま代入するということが不可能であるとして、それ以外のシェルで配列のまま代入することが可能なものはあるのでしょうか?
あるいはもっと他のツールを使った方がよいのでしょうか?

教えていただけないでしょうか?
0380名無しさん@お腹いっぱい。2006/11/18(土) 17:35:51
#/bin/bash

hairetu=(a b c d e f d)

for i in ${hairetu[@]}
do
echo $i
done

exit 0


こんな感じか?
0381名無しさん@お腹いっぱい。2006/11/18(土) 17:39:21
>>380
違うだろ。複数の配列を for 文の要素として使いたいらしいが。
0382名無しさん@お腹いっぱい。2006/11/18(土) 17:40:46
bash Uzzzee.. よそ行け
0383名無しさん@お腹いっぱい。2006/11/18(土) 17:42:03
>内容はfor文のループ変数に配列を代入することは可能なのか?

配列をFor文のループ変数に使えるかってことじゃないのか?
0384名無しさん@お腹いっぱい。2006/11/18(土) 17:46:26
どのような結果が得たいのか書けよ
0385名無しさん@お腹いっぱい。2006/11/18(土) 18:01:27
forという事はborne shellだな。
borne shellには配列などというデータタイプは無い。
0386名無しさん@お腹いっぱい。2006/11/18(土) 18:02:55
>>385
"borne shell"って何ですかww
0387名無しさん@お腹いっぱい。2006/11/18(土) 18:13:33
for i in "(1 100)" "(101 200)";do
eval STARTEND=$i
START=${STARTEND[0]}
END=${STARTEND[1]}
echo $START, $END
done

キモい。ただでさえキモい bash でさらにこんなキモいことするのか。
0388名無しさん@お腹いっぱい。2006/11/18(土) 18:22:48
>>387
いんじゃないか。

こんなインチキしか思い付かなかった
perl -e 'foreach $a ([1, 100], [101, 200]) {
 print "$a->[0] $a->[1]¥n";
}'
0389名無しさん@お腹いっぱい。2006/11/18(土) 19:47:14
>>387
evalを使えば何でも出来る。
0390名無しさん@お腹いっぱい。2006/11/18(土) 19:51:45
evalが使えるからってエヴァるな。
0391名無しさん@お腹いっぱい。2006/11/18(土) 20:13:24
>>390
>>339
>>340
0392名無しさん@お腹いっぱい。2006/11/19(日) 01:55:30
>>380-390
みなさん早速にレスいただき恐縮です。外出しておりました。申し訳ありません。
質問したかったのは>>383さんが書かれたとおりです。
>>387さんが書かれたスクリプトでうまくいきました。ありがとうございました。

ところで、配列がサポートされているCシェル、Kシェル、Zシェルでは「for文(に相当する構文)のループ変数に配列を用いること」は可能でしょうか?
0393名無しさん@お腹いっぱい。2006/11/19(日) 07:47:13
>>366
ありがとうございます。
とりあえず片付いたらそれをテストしてみます。
0394名無しさん@お腹いっぱい。2006/11/19(日) 09:28:12
>>366
関数を作らず、変数を1個使ってうまくいきました。

0395名無しさん@お腹いっぱい。2006/11/19(日) 15:33:55
>>366
項目が1万越していたら終らない
0396名無しさん@お腹いっぱい。2006/11/19(日) 20:32:30
>>395
そういう状況で適用したい人は自分で考えるってことで。
0397名無しさん@お腹いっぱい。2006/11/20(月) 17:41:38
今使っている参考書に
: ${VAR:="exit script because VAR is not set."}
と記述すると、VARに値が設定されていない場合に実行中のスクリプトを
終了させることが出来る、と書かれているのですがうまくいきません。
これはなぜなんでしょうか?わかる方教えてください。お願いします。
0398名無しさん@お腹いっぱい。2006/11/20(月) 18:01:09
>>397
なんていう本? そんな間違った本捨てて、別の買え。
正しくは、

: ${VAR:?"exit script because VAR is not set."}
03993972006/11/20(月) 18:43:53
>>398
早速のレスありがとうございました。うまくいきました。
けっこう長い時間これで悩んでたので本当に助かりました。ありがとうございました。

ちなみに参考書は
「入門UNIXシェルプログラミング シェルの基礎から学ぶUNIXの世界」
です。他のところで聞いたらこれを勧められたのですがダメな本なのでしょうか…。
0400名無しさん@お腹いっぱい。2006/11/20(月) 19:22:21
ミスプリ程度の間違いだろ。
0401名無しさん@お腹いっぱい。2006/11/20(月) 19:44:22
別紙とかで訂正とかないのか?
0402名無しさん@お腹いっぱい。2006/11/20(月) 19:46:36
http://www.sbcr.jp/books/products/detail.asp?sku=4797321946
のフォームで報告しときゃいいんじゃね。
0403名無しさん@お腹いっぱい。2006/11/20(月) 21:19:03
ミスプリなのか書き間違いなのかは知らんが、おかしいと思ったらマニュアル
読んで調べるぐらいした方がいいぞ。
■ このスレッドは過去ログ倉庫に格納されています