トップページ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 でトレースしましょう。
0245名無しさん@お腹いっぱい。2006/10/26(木) 10:32:49
>>244
2 回目の read はいらんだろ。変換前の値もつけとけばいい。

しかし、なんで標準外の date の -d オプションにこだわるんだ?
while read dt tm user; do
  tmp=${dt%/*}; x=${tmp#*/}
  echo $user ${dt##*/}/${dt%%/*}/$x $tm $dt $tm $user
done | sort -r | uniq -f 5 | cut -d ' ' -f 4-
入力順も保存したいなら
cat -n |
while read num dt tm user; do
  tmp=${dt%/*}; x=${tmp#*/}
  echo $user ${dt##*/}/${dt%%/*}/$x $tm $num $dt $tm $user
done | sort -r | uniq -f 6 | sort -nk 4 | cut -d ' ' -f 5-
0246名無しさん@お腹いっぱい。2006/10/27(金) 22:48:53
2 つのフィールドを持つ A, B ファイルを結合したいのですが、
一方にしか存在しないフィールドはそのまま出力し、
それ以外は第二フィールドを B に変更したいのです。

例えば次のようなファイルを:
foo 0
bar 0
--
foo 1
qux 1

次のように:
foo 1
bar 0
qux 1

順序は不同なのでソートしちゃっても構いません。
どのような方法があるでしょうか?
0247名無しさん@お腹いっぱい。2006/10/27(金) 23:33:05
cut -f1 -d ' '
uniq
uniq -d

を使って適当に。
0248名無しさん@お腹いっぱい。2006/10/28(土) 17:24:58
>>247 ありがとうございます。
join(1) あたりで出来るかと思ったんですが、いまいち上手くいかないorz
もう少し頑張ってみます。
0249名無しさん@お腹いっぱい。2006/10/28(土) 20:20:34
for aa in a b; do cat $aa |sed -e "s/ / $aa /;"; done |\
sed -e "s/\([^ ]*\).*/& \1/;" |sort -r |uniq -3 |cut -d" " -f 1,3
0250名無しさん@お腹いっぱい。2006/10/29(日) 08:35:39
あなたが書いた最大のシェルスクリプトの文字数は?
0251名無しさん@お腹いっぱい。2006/10/29(日) 09:16:24
http://life7.2ch.net/test/read.cgi/diet/1158326490
美容版の糞コテ「たお」は自慢大好き
その自慢もたいしたこと無いのに得意気で
見ててとても痛い糞コテ
批判に対しては必ず長レスで的外れに噛み付いてくる
とっても哀れなやつ
http://life7.2ch.net/test/read.cgi/diet/1158326490
0252名無しさん@お腹いっぱい。2006/10/30(月) 16:26:50
縦書き
mm=80; nn=3;

pp=`printf "%0${nn}d" 0`;
for ((aa=0; aa<$mm; aa++)); do
bb[$aa]=$pp$aa;
done;

for ((aa=-$nn; aa<0; aa++)); do
for ((aa1=0; aa1<$mm; aa1++)); do
echo -n ${bb[$aa1]:$aa:1};
done;
echo;
done;
0253名無しさん@お腹いっぱい。2006/10/30(月) 18:29:51
>>252
脈絡なく何を言いたいかわからんが、
zsh依存乙。

bashですら動かない依存スクリプトは、Bourne-sh互換に書き直して出直すこと。
02542462006/10/30(月) 20:11:41
>>249
なるほど. そういう使い方もあるのですね.
これで上手くいきそうです. ありがとうございました.
0255名無しさん@お腹いっぱい。2006/10/31(火) 01:16:24
>>252
俺の環境ではこうなったが、これでいいのか?

00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000001111111111222222222233333333334444444444555555555566666666667777777777
01234567890123456789012345678901234567890123456789012345678901234567890123456789

$ bash --version
GNU bash, version 3.1.17(9)-release (i686-pc-cygwin)
0256名無しさん@お腹いっぱい。2006/11/01(水) 17:56:32
不定個のコマンドをパイプで繋げて処理する方法はないですか?
0257名無しさん@お腹いっぱい。2006/11/01(水) 18:10:02
>>256
文字列でパイプライン組み立ててevalしる
0258名無しさん@お腹いっぱい。2006/11/02(木) 09:32:24
evalを使えば何でも出来る。
0259名無しさん@お腹いっぱい。2006/11/05(日) 01:14:11
ディレクトリ名の引数から末尾の名前だけを取り出すにはどうすればいいでようか?
たとえば

% hoge /usr/local/bin
bin
% hoge ~/etc
etc
% pwd
/usr/local/bin
% hoge ..
local

みたいな。
0260名無しさん@お腹いっぱい。2006/11/05(日) 01:18:31
x=$(cd "$dir"; pwd); x=${x##*/}
0261名無しさん@お腹いっぱい。2006/11/05(日) 01:23:01
basenameは反則?
0262名無しさん@お腹いっぱい。2006/11/05(日) 01:37:00
退場
0263名無しさん@お腹いっぱい。2006/11/05(日) 13:35:39
hoge=/usr/local/bin/allneeded
% echo $hoge:r
/usr/local/bin/allneeded
% echo $hoge:t
allneeded

csh/tcsh/zsh だがね。sh だったら basename だろ?
0264名無しさん@お腹いっぱい。2006/11/05(日) 13:42:20
相対パスがやっかいだな。
0265名無しさん@お腹いっぱい。2006/11/05(日) 14:39:54
basenameはPOSIXにすらあるんだからどんどん使うべし。
0266名無しさん@お腹いっぱい。2006/11/05(日) 18:18:11
basename なんて使わなくても >>260 でいいじゃないか
0267名無しさん@お腹いっぱい。2006/11/05(日) 18:44:46
bshで使えないからダメ。
0268名無しさん@お腹いっぱい。2006/11/05(日) 18:49:29
POSIX sh で使えるから別にいいんじゃん
0269名無しさん@お腹いっぱい。2006/11/05(日) 22:37:29
これならBourne Shellで動く。


arg=/usr/local/bin
(IFS=/; set $arg; shift `expr $# - 1`; echo "$1")
0270名無しさん@お腹いっぱい。2006/11/06(月) 00:12:33
basenameは外部コマンドでも存在「しなければならない」。
たとえ意味がなくてもcdが外部コマンドとして存在「しなければならない」のと同じ。
だから、>>266-267,269の言ってることは意味がない。考えるだけ無駄。
0271名無しさん@お腹いっぱい。2006/11/06(月) 00:28:55
> たとえ意味がなくてもcdが外部コマンドとして存在「しなければならない」のと同じ。
どこの世界の話だ?

SUSv3:
> Since cd affects the current shell execution environment, it is always
> provided as a shell regular built-in.
0272名無しさん@お腹いっぱい。2006/11/06(月) 00:51:53
お前の挙げているSUSv3の世界だよwwww

1.13 Built-In Utilities
However, all of the standard utilities, including the regular
built-ins in the table (中略) shall be implemented in a manner so that
they can be accessed via the exec family of functions as defined in
the System Interfaces volume of IEEE Std 1003.1-2001 and can be
invoked directly by those standard utilities that require it (後略)
0273名無しさん@お腹いっぱい。2006/11/06(月) 01:36:29
どこにも「外部コマンドとして存在しなければならない」なんて書いてないが?
そこで要求されてるのは
they can be accessed via the exec family of functions
つまり execve 等から呼出せれば外部コマンドである必要なんてない。
0274名無しさん@お腹いっぱい。2006/11/06(月) 01:39:02
はいはい、そうでちゅよねーwwww
0275名無しさん@お腹いっぱい。2006/11/06(月) 01:49:28
"in a manner"というニュアンスなんで、なければならない
というほどでもないのでは?外野ですが。
0276名無しさん@お腹いっぱい。2006/11/06(月) 02:03:21
in a mannerはso that節につながるだけだよ。
むしろ"shall be"という強い書き方に注目しないといけない。

んで、exec系の関数の説明には
The exec family of functions shall replace the current process image
with a new process image. The new image shall be constructed from a
regular, executable file called the new process image file.
と書いてあるわけだ。ちゃんと規格書をあたる点は誉めてやってもいいが、
かなり調べ方が足りないゾwwww
0277名無しさん@お腹いっぱい。2006/11/06(月) 02:29:57
はいはい、そうでちゅよねーwwww
0278名無しさん@お腹いっぱい。2006/11/06(月) 02:34:32
cd の方に nohup cd なんて例があるくらいだから
cd が実行ファイルとして存在することを仮定してるんじゃないかな。
0279名無しさん@お腹いっぱい。2006/11/06(月) 02:45:36
うん、仮定っていうより、そう決まってるんだけどね。
ていうか、それはもう>>272ですんだ話。
0280名無しさん@お腹いっぱい。2006/11/06(月) 03:25:25
相対パスが多少面倒だな。
0281名無しさん@お腹いっぱい。2006/11/06(月) 06:42:17
マタマタごジョーダンを
0282名無しさん@お腹いっぱい。2006/11/06(月) 08:51:00
cdが外部コマンドになってると何がうれしいのか俺にもわかるように説明してくれないか。
POSIXで決まってるからというのはなしね。なぜそう決めたかの背景を知りたい。
0283名無しさん@お腹いっぱい。2006/11/06(月) 09:49:49
>>282
POSIXで誤ってそう決めてしまった、という説が有力。

後付けの言い訳としては、対象ディレクトリに実際に cdできるかどうかを
テストする目的で使える(返り値で結果判定)、と説明されているが、そんな例あまりないし、
内部コマンドの cdで、
(cd hoge) とやれば済む話。
0284名無しさん@お腹いっぱい。2006/11/06(月) 17:49:06
% cat /usr/bin/cd
#!/bin/sh
# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.1 2002/07/16 22:16:03 wollman Exp $
# This file is in the public domain.
${0##*/} ${1+"$@"}
0285名無しさん@お腹いっぱい。2006/11/06(月) 18:31:56
>>284

${1+"$@"} って、 "$@" だけでよくねぇ?
0286名無しさん@お腹いっぱい。2006/11/06(月) 23:17:07
>>283
cdだけならそれでも説明はつくんだけど、外部コマンドとしても存在する
ビルトインコマンド(regular built-in utility)は、cdだけじゃないのがややこしいところ。

alias, bg, cd, command, false, fc, fg, getopts, jobs, kill, newgrp,
pwd, read, true, umask, unalias, wait

謎だよなあ… fcやcommandに至っては、何のために存在するのかすらわからない。
0287名無しさん@お腹いっぱい。2006/11/06(月) 23:33:12
シェル関数はbシェルにも存在するから、外部コマンドのcommandはそれなりに意味があるんじゃね?
0288名無しさん@お腹いっぱい。2006/11/06(月) 23:40:49
うう、わからない…
もうちょっとやさしくして
0289名無しさん@お腹いっぱい。2006/11/06(月) 23:42:48
意味のある外部コマンド:
(cronとか、inetdとかから直接起動されるとか、
env/nice/time/nohupとかの引数として起動するために外部である必要があるもの)

command, false, kill, newgrp, pwd, true


意味のない外部コマンド:
alias, bg, cd, fc, fg, getopts, jobs, read, umask, unalias, wait
0290名無しさん@お腹いっぱい。2006/11/06(月) 23:46:26
それより、教育上、「:」の外部コマンド版 (/bin/:)を
配置するべきだ。/bin/[ はあるのだから。
0291名無しさん@お腹いっぱい。2006/11/06(月) 23:48:12
>>290
教育上って、どういうこと?
0292名無しさん@お腹いっぱい。2006/11/06(月) 23:50:18
Solarisには /bin/[ (/usr/bin/[)は無い。教育に適さないOSだ。
0293名無しさん@お腹いっぱい。2006/11/07(火) 01:39:01
へーーー
0294名無しさん@お腹いっぱい。2006/11/07(火) 08:57:14
waitはpidを引数にとれるからまったく意味がないわけでもないんじゃないか。
0295名無しさん@お腹いっぱい。2006/11/07(火) 18:29:53
初心者な質問ですが
シェルの格納場所を取得できるような変数とかコマンドとか
誰か知りませんか?
0296名無しさん@お腹いっぱい。2006/11/07(火) 19:25:20
>>295
cat /etc/shells
0297名無しさん@お腹いっぱい。2006/11/07(火) 20:11:55
which じゃないの ?
0298名無しさん@お腹いっぱい。2006/11/07(火) 20:19:08
>>297
whichは cshのコマンド。それを言うなら type。

typeでは、type shとかやって、/bin/sh とかの PATHは得られるけど、
「この OSにインストールされていて使えるシェルを調べたい」
という質問の回答としては不適当。
0299名無しさん@お腹いっぱい。2006/11/07(火) 20:26:51
>>294
外部コマンドとして起動したwaitだと、どんな pidに対しても、

wait: pid 1234 is not a child of this shell

と言われるが、それに意味あるのか??
0300名無しさん@お腹いっぱい。2006/11/07(火) 22:14:52
bash入門講座してください
0301名無しさん@お腹いっぱい。2006/11/07(火) 22:38:30
>>300
つ言いだしっぺの法則。
0302名無しさん@お腹いっぱい。2006/11/08(水) 01:03:49
起動スクリプト読んでたら set start ってあったんだけどこれ何?
0303名無しさん@お腹いっぱい。2006/11/08(水) 01:13:19
>>301
講師探してます
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 とかを使う。
■ このスレッドは過去ログ倉庫に格納されています