トップページ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 でトレースしましょう。
0466名無しさん@お腹いっぱい。2006/11/24(金) 14:40:40
hoge.192.168.0.1
という文字列から 192.168.0.1 の部分を抜き出したいのですが
どのようにすればよいのでしょうか?
0467名無しさん@お腹いっぱい。2006/11/24(金) 14:46:23
$( )や配列、算術演算は使っとけ。
スクリプトが読みやすくなるから。
0468名無しさん@お腹いっぱい。2006/11/24(金) 15:02:08
>>466
↓を実行。標準入力から hoge.192.168.0.1 の文字列を読ませる。

IFS=. read hoge a b c d
echo $a.$b.$c.$d


>>467
そういう理由は軟弱。却下。
0469名無しさん@お腹いっぱい。2006/11/24(金) 15:16:23
読み易さに勝るポータビリティはない。
0470名無しさん@お腹いっぱい。2006/11/24(金) 15:20:32
>>469
???? 「読み易い」って、書き直すってこと?
そもそもポータビリティの意味を誤解してます。
0471名無しさん@お腹いっぱい。2006/11/24(金) 18:28:43
/bin/shが$()を扱えない環境ってどれ?
0472名無しさん@お腹いっぱい。2006/11/24(金) 18:37:28
>>471
伝統的なBourne shellが/bin/shであるような環境では使えない。
Solarisとか。
0473名無しさん@お腹いっぱい。2006/11/24(金) 20:07:45
Solarisなら無視して桶。ユーザーほとんどいないし。
そのうちSolaris側が /bin/sh -> bash にリンクしてくる可能性すら考えられる。
0474名無しさん@お腹いっぱい。2006/11/24(金) 21:54:31
sh の実体が ksh になることはあっても bash になることはないと断言しよう。
0475名無しさん@お腹いっぱい。2006/11/24(金) 22:29:24
いまさらkshもなかろうw
0476名無しさん@お腹いっぱい。2006/11/25(土) 02:19:05
アプリの関係でcshで書いてます。
------------------------
#!/bin/csh
set i=`ps axwww|grep -i aaaa | grep -v grep | wc -l`
echo $j
------------------------

aaaaは存在しないプロセス。
この実行結果、
「0」でなくて「1」になるのはなぜですか?
04774762006/11/25(土) 02:23:46
訂正です。
------------------------
#!/bin/csh
set i=`ps axwww|grep -i aaaa | grep -v grep | wc -l`
echo $i
------------------------
0478名無しさん@お腹いっぱい。2006/11/25(土) 02:41:37
>>477
パイプで一気に処理している中身を細切れにして中身を確認してみろ。

あと、pgrep があるならそっちを使った方がいい。
04794762006/11/25(土) 02:55:11
>>478
レスありがとうございます。
パイプ grep -v /bin/csh の追加が必要でした。
0480名無しさん@お腹いっぱい。2006/11/25(土) 03:22:50
せめて csh -f にしろや。
0481名無しさん@お腹いっぱい。2006/11/25(土) 19:03:08
ifconfigを使ってネットワークにつないでいるインターフェースのデバイス名だけを
取得するにはどう記述すればよいのでしょうか?
0482名無しさん@お腹いっぱい。2006/11/25(土) 19:33:35
>>481
OSによってifconfigの出力書式が違うかも知れんが、
↓みたいな感じで行ける。

ifconfig -a | sed -n 's/^\([^ ][^ ]*\).*/\1/p'
0483名無しさん@お腹いっぱい。2006/11/25(土) 19:37:28
HP-UXだと、
lanscanだな。
0484名無しさん@お腹いっぱい。2006/11/25(土) 20:30:33
FreeBSD だと ifconfig -l で。
0485名無しさん@お腹いっぱい。2006/11/25(土) 21:05:38
http://photos.nikkansports.com/sports/synchro/photo/P2006091401786.jpg
0486名無しさん@お腹いっぱい。2006/11/26(日) 14:56:47
>>397-400
ミスプリ程度の間違いじゃない、ダメ本みたいだ。
こっちの書き込みによると。

http://pc8.2ch.net/test/read.cgi/linux/1065059126/794
0487名無しさん@お腹いっぱい。2006/11/27(月) 02:12:37
自分で作ったシェルスクリプトの引数に"*"と指定したら、
クォートされずに*が展開されて位置パラメタに入っちゃうんだけどなんでなんでだろ。
echoとかの組み込みコマンドだと上手くクォートされるんだけどなぁ。
0488名無しさん@お腹いっぱい。2006/11/27(月) 07:20:57
>>487
どこかでクォートし忘れているって事なんだろうな。
そのスクリプトを晒してみな
0489名無しさん@お腹いっぱい。2006/11/29(水) 21:31:14
age
0490名無しさん@お腹いっぱい。2006/11/29(水) 21:52:55
noglob とかそういうのを使ってみたり
0491名無しさん@お腹いっぱい。2006/11/29(水) 22:31:42
>>490
それは csh。cshは帰れ。

/bin/shでは set -f
ただし、ちゃんとクォートすれば set -f は必要ない。
0492名無しさん@お腹いっぱい。2006/11/30(木) 21:22:53
$10以降の引数ってどのshでも使えるんでしょうか?
0493名無しさん@お腹いっぱい。2006/11/30(木) 21:28:40
>>492
Bourne Shellでは $10以降は使えない。
bashとかkshとかでは使えるが、${10} とする必要あり。
zshでは $10 でも使える。
0494名無しさん@お腹いっぱい。2006/11/30(木) 21:31:31
$10は
${1}0と解釈されるから、${10}としないとだめ。
0495名無しさん@お腹いっぱい。2006/11/30(木) 21:33:03
zsh は使用料が 10 ドルから、っていう意味?
0496名無しさん@お腹いっぱい。2006/11/30(木) 21:33:29
>>494
Bourne Shell では ${10} でもダメ。逆に zsh では $10 でも桶。
04974922006/12/01(金) 00:40:19
皆さんありがとうございます。
shで最後の引数を取得したかったのですが、無理なのですね。
0498名無しさん@お腹いっぱい。2006/12/01(金) 00:48:11
shiftするとかいくらでも方法はある。
0499名無しさん@お腹いっぱい。2006/12/01(金) 02:24:15
GNU Autoconf付属のinfo内の
"Portable Shell:: Shell script portability pitfalls"
が非常に詳しくて面白い。よくこれだけまとめあげたものだ
0500名無しさん@お腹いっぱい。2006/12/02(土) 01:04:32
>>470
人間レベルポータビリティが高いということだと思う。

狭い範囲でのポータビリティを満たすためにトリックを駆使するより、
移植性のない明快な方法複数のほうが意図が読めるから未知プラット
フォームに持って行きやすい(ことがある)。
0501名無しさん@お腹いっぱい。2006/12/02(土) 01:16:48
>>500
そういう概念を表すためにわざわざ、可読性(readability)とか
保守性(maintainability)とかいう用語が存在してるでしょうに。
0502名無しさん@お腹いっぱい。2006/12/02(土) 01:23:51
そうなんだけど、人間を計算機の一種に見立てて、可読性と
移植性は似てる(生体計算機上で移植性がある=可読性)と
元レスはいいたかったんでないの?
0503名無しさん@お腹いっぱい。2006/12/02(土) 20:53:48
ABC
DEF
GHI
JKL
MNO

という文字列を
DEF
GHI
JKL
という部分をごっそり抜いて、
ABC
hogehoge1
hogehoge2
hogehoge3
JKL
としたいのですが、
何か良い案はないでしょうか?
0504名無しさん@お腹いっぱい。2006/12/02(土) 21:06:34
MNOはどこに消えたのか?
0505名無しさん@お腹いっぱい。2006/12/02(土) 21:08:49
>>504
それくらい察してやれ。
それより、DEFやGHIなどの文字列があるものとして置換していいのか、
それとも2行目から4行目までを置換するという意味なのか、
質問の意味が複数解釈できるね。
0506名無しさん@お腹いっぱい。2006/12/02(土) 21:11:49
>>505
全くそのとおり。
0507名無しさん@お腹いっぱい。2006/12/02(土) 22:17:15
「ごっそり抜い」 たはずのJKLは
なぜ残っているのか?
0508名無しさん@お腹いっぱい。2006/12/02(土) 22:24:14
>>507
だからぁ、察してやれよ、MNOの書き間違いだろ。
0509名無しさん@お腹いっぱい。2006/12/02(土) 22:27:07
これでいいか? gawk だが

$ cat minus.awk
#!/usr/bin/gawk -f

ARGIND == 1 {
    minus[ $0 ] = sprintf("hogehoge%d",FNR)
    next
}
$0 in minus {
    print minus[ $0 ]
    next
}
{
    print
}
$ gawk -f minus.awk (抜くキーワードを書いたファイル)  (抜かれる方のファイル)
0510名無しさん@お腹いっぱい。2006/12/02(土) 22:37:23
すみません。改めます。
シェルスクリプトでやりたいです。

ABC
DEF
GHI
JKL
MNO

という文字列を
DEF
GHI
JKL
という部分をごっそり抜いて、
ABC

MNO
の間に特定の文字列を挿入して、
ABC
hogehoge1
hogehogeXX
hogeYY
MNO
としたいのですが、
何か良い案はないでしょうか?
よろしくお願いします。
0511名無しさん@お腹いっぱい。2006/12/02(土) 22:43:52
>>510
↓ほれ、これでいいかい? シェルだけでやったよ。外部コマンドは使ってない。

while read line
do
case $line in
ABC|MNO) echo "$line";;
DEF) echo hogehoge1;;
GHI) echo hogehogeXX;;
JKL) echo hogeYY;;
esac
done < input_file
0512名無しさん@お腹いっぱい。2006/12/02(土) 22:55:31
>>510
cat aa |sed -e '/DEF/,/JKL/c\
hogehoge1\
hogehogeXX\
hogeYY'
0513名無しさん@お腹いっぱい。2006/12/02(土) 22:56:21
>>510
>>505は読んだのかw?
0514名無しさん@お腹いっぱい。2006/12/02(土) 22:56:33
>>512
お約束のcatが無駄です。あと、シェルだけでやりたいと申されております。
0515名無しさん@お腹いっぱい。2006/12/02(土) 23:02:26
>>510
> DEF
> GHI
> JKL
> という部分をごっそり抜いて、
これで人に通じると思ってるの? ゆとり世代?

1 ABCの次の行からMNOの前の行まで。
2 2行目から4行目まで
3 2行目から最終行-1まで
4 DEFまたはGHIまたはJKL
5 1文字目がAかM以外
6 1文字目がDまたはGまたはJ
...
0516名無しさん@お腹いっぱい。2006/12/02(土) 23:06:31
>>510
お客様、仕様書が曖昧で作業に入れませんのでご確認をお願いいたします。
こいういうことでしょうか?

START-STRING
AAAA
BBB
CC
END-STRING



START-STRING
REPLACED-1
REPLACED-2
REPLACED-3
END-STRING

とする。
START-STRINGとEND-STRINGに囲まれた複数行を置換するんでしょ?
読み取れないのは、AAAA,BBB,CCの部分は何でも良いのか、
hogehoge1,hogehogeXX,hogeYYの部分は固定なのか、AAAA,BBB,CCそれぞれに
対応させて置換するのか。
05175102006/12/02(土) 23:06:52
1 ABCの次の行からMNOの前の行まで。
ですので、
512
でほぼ解決です。
ありがとうございました。
説明がわかりにくくてご迷惑をお掛けしました。
また出直します。
0518名無しさん@お腹いっぱい。2006/12/02(土) 23:09:28
>>517 池沼?

>>512
DEFとJKLではさまれている行だろうが、どこが
> ABCの次の行からMNOの前の行まで。
なんだよ。
0519名無しさん@お腹いっぱい。2006/12/02(土) 23:23:28
>>518
「ほぼ」って言ってるから良いんじゃない?
sed -e '/ABC/,/MNO/c¥
ABC¥
hogehoge1¥
hogehogeXX¥
hogeYY¥
MNO'
程度の修正は誤差であるというくらい理解したんじゃないかna
0520名無しさん@お腹いっぱい。2006/12/02(土) 23:32:03
for (i=0; i<10; i++) ; do
echo "$i"
done
Bourne Shellでも動きますか?
0521名無しさん@お腹いっぱい。2006/12/02(土) 23:32:35
むりでーす。
05225202006/12/02(土) 23:32:52
間違えた

for ((i=0; i<10; i++)) ; do
echo "$i"
done
Bourne Shellでも動きますか?
0523名無しさん@お腹いっぱい。2006/12/02(土) 23:38:40
>>522 はBourne shでは使えない。

for i in 1 2 3 4 5 6 7 8 9; do
echo "$i"
done

が最もポータブル。
seqやjotはあえて使わない。
0524名無しさん@お腹いっぱい。2006/12/02(土) 23:52:40
問題の出し方と内容がム板と犬板のシェルスクリプトスレに現れた奴に似てる
0525名無しさん@お腹いっぱい。2006/12/02(土) 23:57:42
>>523
exprくらいは使ってもいいじゃね?
0526名無しさん@お腹いっぱい。2006/12/05(火) 17:00:20
aaa='"AAA AAA" BBB'
for n in $aaa; do
echo "$n"
done
だと
"AAA
AAA"
BBB

となるのですが、これを
"AAA AAA"
BBB
とするにはどうすればいいのでしょうか?
0527名無しさん@お腹いっぱい。2006/12/05(火) 17:12:42
>>526
for n in "$aaa"

常に "$hoge" みたいに " " を付ける癖を付けろ。
0528名無しさん@お腹いっぱい。2006/12/05(火) 17:13:44
>>527
欲嫁。それだと逆に全部つながっちゃうが、、
0529名無しさん@お腹いっぱい。2006/12/05(火) 22:58:15
>>526
ゴリ押し

aaa='"AAA AAA" BBB'
while test "x$aaa" != x
do
 case $aaa in
 \"*\"\ *)
  echo "$aaa" | sed 's,^\("[^"]*"\).*,\1,'
  aaa=`echo "$aaa" | sed 's,^"[^"]*" *,,'` ;;
 *\ *)
  echo "$aaa" | sed 's, .*,,'
  aaa=`echo "$aaa" | sed 's,[^ ]* *,,'` ;;
 *)
  echo "$aaa"; break ;;
 esac
done
0530名無しさん@お腹いっぱい。2006/12/05(火) 23:20:23
" " ではさんだやつは " " も消しちゃいかんという仕様がなぁ
05315262006/12/06(水) 13:55:07
う、そんなにややこしいことなんですか?
精進します
0532名無しさん@お腹いっぱい。2006/12/06(水) 16:54:03
変数に入っている最初の1文字だけを取り出したいんですけど、なにか良い方法は無いでしょうか?
0533名無しさん@お腹いっぱい。2006/12/06(水) 17:48:45
>>532
foo=$(echo $bar | sed -e 's!\(^.\).*!\1!')
0534名無しさん@お腹いっぱい。2006/12/06(水) 18:48:23
>>532
${var%%${var#?}}
05355322006/12/06(水) 20:52:29
>>533, >>534
ありがとうございます。

自分なりに無い知恵を絞って出した答えは
foo=`perl -e "print substr('$var', 0, 1)"`
だったんですが、>>534には感動しました。
0536名無しさん@お腹いっぱい。2006/12/06(水) 21:02:54
>>534 は Bourne Shell で動かないからポータブルじゃない。
↓がお勧め。

printf '%c\n' "$var"
0537名無しさん@お腹いっぱい。2006/12/06(水) 21:42:29
>>532
echo ${var:0:1}
0538名無しさん@お腹いっぱい。2006/12/06(水) 21:48:39
>>537
bash/ksh依存乙。
0539名無しさん@お腹いっぱい。2006/12/06(水) 21:54:07
これならかなりクラシックな環境でも可
foo=`expr "$bar" : '^\(.\)'`
0540名無しさん@お腹いっぱい。2006/12/06(水) 22:49:45
うぉー、さらに増えてる。
みなさん、ありがとうございます。

printfが存在を知ったり、exprの使い方がわかったりと、かなり勉強になりました。
ただ、>>539の正規表現は'\(.\)'`でいいみたいです。
^は正規表現の意味を持っていないので無視される。みたいなワーニングがでました。
0541名無しさん@お腹いっぱい。2006/12/06(水) 23:48:50
echo "$var" | cut -c1 は?
0542名無しさん@お腹いっぱい。2006/12/07(木) 00:01:29
grep hoge * | nkf -e > out というコマンドを
xargs を使った形に書き換えたいんですが、どう書けばいいんでしょうか?
0543名無しさん@お腹いっぱい。2006/12/07(木) 00:25:47
find * -print | xargs grep hoge | nkf -e > out?
ていうか grep とか grep -r で済むなら xargs の出番なさそう。
05445422006/12/07(木) 01:04:05
あ、すいません。説明を間違えました。
ファイルの文字コードがそれぞれ違う可能性があるから、
for i in *; do grep hoge $i |nkf -e >> out; done
と同じことをしたいのでした。
この場合も for の行で外部コマンドを呼んでいないから
ARG_MAX の制限はないようですね。
xargs で起動するコマンドの中にパイプやリダイレクトを
入れることはできるか、と聞きたかったのでした。
0545名無しさん@お腹いっぱい。2006/12/07(木) 02:08:55
一応

 find * ... -print | xargs -n 1 sh -c 'grep hoge $0 | nkf -e' > out

とかでもできるけど、

 find * ... -print | xargs nkf -e | grep hoge > out

の方がエレガントなような。でも、 file: line 形式で grep 結果を
出したいなら、ついでに * 展開で溢れるケースも考慮して

 find . ... -print | while read i; do grep hoge $i | nkf -e; done > out

しかし ARG_MAX の心配をするほどファイルがあるなら

 find . ... -print | xargs perl -MJcode -lne 'print jcode("$ARGV: $_")->euc if /hoge/' > out

がちょっと長いけど速そう。
0546名無しさん@お腹いっぱい。2006/12/07(木) 09:27:20
とりあえずお約束として
find -print0
xargs -0
にしる、と突っ込んでおくべきなのだろうか
0547名無しさん@お腹いっぱい。2006/12/07(木) 13:53:37
お約束だけど GNU のは入れられなかったりとか
仕様バグがどうこうとか。
0548名無しさん@お腹いっぱい。2006/12/12(火) 15:03:35
PPPoA で接続してるんですが、モデムのログがほとんど残してもらえず(容量少い)
不安定な回線なのにどの程度不安定なのか把握できず困ってます
切断されてもすぐ再接続されるわけですが、これを記録しておきたいと思います
ping 撃ちまくるのはわかりますが、切断された時と再接続された時を記録するには
どんなスクリプト書けばいいですか?
0549名無しさん@お腹いっぱい。2006/12/12(火) 19:53:11
1.txt
↓内容
1
11
111

cat 1.txt | grep 11
とやると
11と111の2つがヒットしてしまいます(^_^;)
11の行だけを抜き出す方法はないですかね?
0550名無しさん@お腹いっぱい。2006/12/12(火) 20:14:17
>>549
grep -w 11
0551名無しさん@お腹いっぱい。2006/12/12(火) 20:47:34
>>549
catが無駄です、のお約束。
0552名無しさん@お腹いっぱい。2006/12/12(火) 21:11:23
>>551
あんたマイナーになってるよ。
0553名無しさん@お腹いっぱい。2006/12/12(火) 22:06:51
>>549
grep -x 11 1.txt
0554名無しさん@お腹いっぱい。2006/12/13(水) 10:08:05
>>551 かっこいいから俺も会社で真似して渋く言ってみます
0555名無しさん@お腹いっぱい。2006/12/13(水) 10:26:21
zcatとかgzcatとかbzcatとかに置き換えるときに、
修正する文字数が少なくなるという、多大なメリットがある。
0556名無しさん@お腹いっぱい。2006/12/13(水) 10:30:43
フィルタだってことがわかりやすいしな。
別に咎めるほどのことではないと思う。
0557名無しさん@お腹いっぱい。2006/12/13(水) 11:39:14
>>555
本気で言ってんのか?
>>549のような使い捨てのワンライナーなら「修正の手間」も糞もねぇだろ。

元スクリプトが
script
なら、
zcat foo.gz | script
とすりゃいいだけだ。一行の修正も要らないし、zcat決めうちにするより
scriptの汎用性が維持される。
こっちのがツールボックスアプローチとして正しいわな。

必要も無いcatを使ってるスクリプトを見ると、俺は不安になるね。
書き手に変な癖がついてるのか、リダイレクションすら理解して無いのか、
品質上に問題があるんじゃないのかとか。
0558名無しさん@お腹いっぱい。2006/12/13(水) 11:53:26
>>557
使い捨てならわざわざリダイレクトなり引き数なりに
直す必要もないじゃん。
script の汎用性の話は意味がわからん。
0559名無しさん@お腹いっぱい。2006/12/13(水) 12:05:56
使い捨てなら cat のムダくらい気にせんでもいいやんけ。
0560名無しさん@お腹いっぱい。2006/12/13(水) 12:11:06
>>558, 559
使い捨てと言いつつ、
grep -w 11 1.txt
ですむものを、
cat 1.txt | grep -w 11
と書きたがる意図が分からんのだが。

そんなにcatとタイプして無駄にプロセスを一個増やすのが好きか?
変な癖を正当化してるだけじゃないか?
0561名無しさん@お腹いっぱい。2006/12/13(水) 12:18:25
>>560
おれが書いたわけじゃないから
おれも意図はわからんな。

プロセスの減らし方あれこれ考えるより
さっさとスクリプト書きあげて
仕事終わらせちゃった方がいいじゃん。
0562名無しさん@お腹いっぱい。2006/12/13(水) 12:22:26
>>561
こんなことで「あれこれ考え」なきゃいけない時点でバカ。
判って無い証拠。
cat file cmd
が動作するなら
cmd <file
が確実に動作するんだから。
後者は無駄なプロセスが不要でタイプ量も少ない。

大体、タイプ量増えるのに、「さっさと仕事終わらせる」も糞もないだろ。
05635622006/12/13(水) 12:23:13
すまん
cat file | cmd
な。
0564名無しさん@お腹いっぱい。2006/12/13(水) 21:39:54
cat 1.txt | grep -w 11 は中身確かめて加工に合っている。
0565チラシの裏すまそ2006/12/13(水) 22:01:56
漏れは同じファイルに対して何回かgrepしたいときに

cat file | grep KEYWORD1
 ↓
カーソルキーの↑を押して前回のコマンドを表示し、
Deleteキー連打して、新しいキーワードを打ち込んで
 ↓
cat file | grep KEYWORD2
 ↓
以下、繰り返し

こんな感じだけどね。
シェルスクリプトの中では使わんけど。
0566名無しさん@お腹いっぱい。2006/12/13(水) 22:06:27
>>565
単にコマンドラインの右端の文字を変更しやすいという意味なら、

$ < file grep KEYWORD1
$ < file grep KEYWORD2

↑みたいにやれば良い。< fileの部分が左に来てもいいんだよ。パイプは使わない。
■ このスレッドは過去ログ倉庫に格納されています