シェルスクリプト総合 その8
■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。
2007/02/15(木) 14:28:44スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>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 でトレースしましょう。
前スレ
シェルスクリプト総合 その7
http://pc10.2ch.net/test/read.cgi/unix/1157601611/
0123名無しさん@お腹いっぱい。
2007/02/27(火) 17:22:33え?違うんだ、知らなかった
0124名無しさん@お腹いっぱい。
2007/02/27(火) 22:13:24もうwのないkshしかないからなぁ・・・
0125名無しさん@お腹いっぱい。
2007/02/27(火) 22:45:550126名無しさん@お腹いっぱい。
2007/02/27(火) 22:49:25nkf -e < script
nkf -j < script
nkf -s < script
nkf -w < script
で化けないパターンを探してそれに置き換える。
0127名無しさん@お腹いっぱい。
2007/02/27(火) 22:51:13その vi の実体は、賢い vi で、
シェルの ENV がついていけてないとか。
0128名無しさん@お腹いっぱい。
2007/02/28(水) 00:25:54exp 〜 query\"where column_name in\( select col from tableB \) \"
みたくqueryパラメータで副照会できないようなんで
exp 〜 query\"where column_name in\( $COLUMN_NAMES \) \"
なんて具合に変数を埋め込みたい。
シェル変数が展開されてからexportに渡されるようにする方法ないかな?
0129名無しさん@お腹いっぱい。
2007/02/28(水) 00:54:58nkf -g が無いバージョンかな?
0130名無しさん@お腹いっぱい。
2007/02/28(水) 01:00:16nkf -wができることに最近気づいたくらいアップデートしてなかったんで。
0131名無しさん@お腹いっぱい。
2007/02/28(水) 09:20:31中国の恐さにぞっとすること請け合い。
あいつら本気で日本を侵略する気だ。
そして、手下はやはり立命館?
0132118
2007/03/01(木) 04:39:27すんません書き漏れです
知りたいのはsh-posixとksh88の機能差です
kshにksh88とksh93があってかなり違うってのは知ってるというか
kshの情報には大抵2つのバージョンの比較が併記されてるもんだし
ただsh-posixとksh88の比較は見たことがないし違いも見つけられなかったもんで
0133名無しさん@お腹いっぱい。
2007/03/01(木) 06:28:17・ パターンマッチングがegrep相当に拡張
・ [[ ほげ ]] (中身をメタキャラ解釈しないtestのようなもの)
・ 配列が使えた
・ emacs風コマンドライン編集
ってとこか。なおksh93はksh88と挙動が違ったりバグが直されたりしてるし、
ksh88とpdkshでも機能に違いがあるので、
ksh88で動けば他のkshで動作するとは限らない
0134名無しさん@お腹いっぱい。
2007/03/01(木) 22:50:400135名無しさん@お腹いっぱい。
2007/03/02(金) 00:10:52----
yes
・・・・
・・・
・・・・・
IPアドレス xxx.xxx.xx.54
----
no
・・・・
・・・・・
IPアドレス xxx.xxx.xx.123
----
yes
・・・・・・・・・
・・・・・
IPアドレス xxx.xxx.xx.23
こういうふうに加工したいのですが、
yes,xxx.xxx.xx.54
no,xxx.xxx.xx.123
yes,xxx.xxx.xx.23
・・・・・・・・・
・・・・・
の部分は2行のときもあるし10行のときもあります。
0136135
2007/03/02(金) 00:14:33----
と
IPアドレス
という文字をうまく関連付ければ
加工可能とは思うのですが、
具体的にどうしたらいいのかよくわかりません。
最初の
----
を見つけたら
その次の「IPアドレス」という文字列を捕まえて
xxx.xxx.xx.123を確保する、
という形でしょうか。
うまい方法があればヒントをいただけますか?
cat、more、grep、cut、sedなど一般的なコマンドは習得しています。
0137名無しさん@お腹いっぱい。
2007/03/02(金) 00:19:45めんどくさいから perl 使っちゃうな。
0138名無しさん@お腹いっぱい。
2007/03/02(金) 00:34:22for file in xx*; do echo `sed -n '2p;${s/.* //;p}' $file`; done
csplitってPOSIX標準だっけ?
0139名無しさん@お腹いっぱい。
2007/03/02(金) 00:34:300140名無しさん@お腹いっぱい。
2007/03/02(金) 01:17:000141名無しさん@お腹いっぱい。
2007/03/02(金) 01:52:510142名無しさん@お腹いっぱい。
2007/03/02(金) 02:52:27書いてみて
0143名無しさん@お腹いっぱい。
2007/03/02(金) 08:40:46俺も awk に一票だが、
最初と最後をどうにかすれば grep と sed だけでもできそう。
grep -C 1 -x -e ---- | sed ....
こんな感じで。
0144名無しさん@お腹いっぱい。
2007/03/02(金) 08:56:59N;
N;
N;
s/¥(yes¥|no¥)¥n/¥1 /;
s/----¥n//;
s/--¥n//;
s/IPアドレス //;
p'
改行ってどこのsedでも¥nと書けるのだっけ?
0145名無しさん@お腹いっぱい。
2007/03/02(金) 09:03:200146名無しさん@お腹いっぱい。
2007/03/02(金) 09:10:43/¥(yes¥|no¥)/h;
/IPアドレス /{
H;
x;
s/¥(yes¥|no¥)¥n/¥1/;
s/IPアドレス /,/;
p
}' input
どだっ!?
0147135
2007/03/02(金) 13:43:23僕はまだレベルが低いですし
awkは(難しくて)使えないので
>>143さんの
grep -C
オプションでやってみよと思います。
0148名無しさん@お腹いっぱい。
2007/03/02(金) 15:27:320149名無しさん@お腹いっぱい。
2007/03/02(金) 15:32:33ちょっと、質問が複数の意味に取れるけど、
ls > "$1"/"$2"
ってことか?
0150148
2007/03/02(金) 16:42:330151名無しさん@お腹いっぱい。
2007/03/02(金) 17:25:160152名無しさん@お腹いっぱい。
2007/03/02(金) 17:27:36[ -f ファイル ]
0153名無しさん@お腹いっぱい。
2007/03/02(金) 18:47:440154名無しさん@お腹いっぱい。
2007/03/02(金) 19:02:570155名無しさん@お腹いっぱい。
2007/03/02(金) 19:07:440156名無しさん@お腹いっぱい。
2007/03/02(金) 21:48:30便乗質問ですが、よく、ファイルがあるかどうかを判断するのに、
test -fを使えと言われるのですが、実行しても何も起こりません。
何か設定が必要なのでしょうか?
0157名無しさん@お腹いっぱい。
2007/03/02(金) 21:58:240158名無しさん@お腹いっぱい。
2007/03/02(金) 22:18:29test -fだとレギュラーファイル以外はないものと判定されるぞ。
(デバイスファイルとか、broken symlinkとか、socketとか)
0159名無しさん@お腹いっぱい。
2007/03/02(金) 22:36:23だめーっ。Bourneではtest -eは使えなーい。test -fが定石。
0160名無しさん@お腹いっぱい。
2007/03/02(金) 22:40:520161名無しさん@お腹いっぱい。
2007/03/02(金) 23:04:59何かが起こるように書けばいい。
test を実行しただけで何も起こらないのは当たり前。
0162名無しさん@お腹いっぱい。
2007/03/02(金) 23:15:29test...も [ ... ] もコマンドを実行してる。
実行した結果のステータスコードで分岐するだけ。
0163名無しさん@お腹いっぱい。
2007/03/03(土) 00:09:47>>162の通り、testはシェルの内部コマンドではない。Bourneかどうかは関係ない。
まあそれはそれとして、autoconf infoの"Limitations of Builtins"から抜粋。
POSIXも信用せずにportableにしたいならどおぞ。
... use `test -f' or `test -r'. Do not use `test -x', because 4.3BSD
does not have it. Do not use `test -e' either, because Solaris 2.5
does not have it. To test for symbolic links on systems that have
them, use `test -h' rather than `test -L'; either form conforms to
POSIX 1003.1-2001, but older shells like Solaris 8 `/bin/sh' support
only `-h'.
0164名無しさん@お腹いっぱい。
2007/03/03(土) 00:37:340166名無しさん@お腹いっぱい。
2007/03/03(土) 01:21:37どっかで聞いたことがあるけど、
UNIX黎明期を除けばどのシステムでもシェル組み込みコマンドとして実装されている。
ループで最頻出のコマンドが外部呼び出しじゃ実用的な速度が出ないだろうしね。
0167名無しさん@お腹いっぱい。
2007/03/03(土) 01:30:36KernighanとPikeのUnix Programming Environmentにそういう記述があった希ガス。
UNIX第7版とかの時代の話だが。
0168名無しさん@お腹いっぱい。
2007/03/03(土) 07:57:520170名無しさん@お腹いっぱい。
2007/03/03(土) 12:08:23適当なループ回して試してみろ。
testが内部コマンドである現在のシェルでも、
case使った方が、if [ ... ] よりも若干速いよ。
おそらく、内部コマンドとはいえ、独立したコマンドとして
内部的に実行するオーバーヘッドがtestにはかかるんだろう。
caseの場合はシェル本体が直接解釈するから
オーバーヘッドはtestよりも少ないと。
0171名無しさん@お腹いっぱい。
2007/03/03(土) 14:06:00釣りか天然か... 判断に苦しむ。
0172名無しさん@お腹いっぱい。
2007/03/03(土) 14:20:05釣りじゃないだろ。実験してみろ。time sh -c '...' とかで計れる。
確かに caseの方がちょっと早い。
0173名無しさん@お腹いっぱい。
2007/03/03(土) 14:34:36おかしなところがあるなら具体的に指摘してみればいいじゃない
0174名無しさん@お腹いっぱい。
2007/03/03(土) 14:37:35$ time zsh -c 'for i in `seq 1 10000`; do [ a = a ] && :; done'
real 0m3.051s
user 0m2.705s
sys 0m0.207s
$ time zsh -c 'for i in `seq 1 10000`; do case a in a);;esac; done'
real 0m0.915s
user 0m0.703s
sys 0m0.204s
↓おまけ。[ ]の代わりに [[ ]] にすると、内部コマンドじゃなく、
直接のシェル文法になるので、早くなる。
$ time zsh -c 'for i in `seq 1 10000`; do [[ a = a ]] && :; done'
real 0m1.708s
user 0m1.201s
sys 0m0.402s
0175名無しさん@お腹いっぱい。
2007/03/03(土) 15:41:12おかしなところは推測の部分。単に実装の違いだけでしょ。
ksh(93)
case 0.65 real 0.46 user 0.17 sys
test 0.58 real 0.46 user 0.10 sys
pdksh
case 23.23 real 22.45 user 0.20 sys
test 4.96 real 4.74 user 0.14 sys
bash2
case 3.52 real 3.21 user 0.24 sys
test 4.26 real 3.93 user 0.25 sys
ash
case 0.38 real 0.28 user 0.09 sys
test 0.52 real 0.45 user 0.05 sys
0176名無しさん@お腹いっぱい。
2007/03/03(土) 17:55:22その実装の違いを推測してるのではないか? なので、おかしくない。
ちなみに俺の環境では、kshでもtestよりcaseの方が速かったよ。
0177名無しさん@お腹いっぱい。
2007/03/03(土) 18:59:530178名無しさん@お腹いっぱい。
2007/03/03(土) 20:43:22real 0m1.758s
user 0m1.562s
sys 0m0.160s
bash3 case
real 0m1.515s
user 0m1.341s
sys 0m0.150s
確かに少しだけど、case の方が速いね
0179名無しさん@お腹いっぱい。
2007/03/03(土) 20:53:55いや、[[じゃなくて [ と caseを比較するという話だが。
速い順に、
case > [[ > [
となると思う。
0180名無しさん@お腹いっぱい。
2007/03/03(土) 21:13:01あ、そうだったっけか。
bash3 [
real 0m2.388s
user 0m2.042s
sys 0m0.370s
0181名無しさん@お腹いっぱい。
2007/03/04(日) 21:32:58ディレクトリ名とその中にあるファイルサイズが0のファイル名を出力する。
補足1:ディレクトリが特定されていない場合は、現在シェルが働いているディレクトリ名を出力する。
補足2:もし引数がディレクトリ名でない場合は、すべてのコマンドラインの引数にエラーメッセージを出力する。
補足3:ファイル名の一番最初の文字が、「.」の場合は無視するようにする。
0182名無しさん@お腹いっぱい。
2007/03/04(日) 21:54:16問題に曖昧なところがあるけど、こういうことか?
bashじゃなくてもB-sh共通で動く。
dir=${1-.}
if [ ! -d "$dir" ]; then
echo "$dir is not a directory" 1>&2
exit 1
fi
for f in "$dir"/*
do
if [ ! -s "$f" ]; then
echo "$f"
fi
done
0183名無しさん@お腹いっぱい。
2007/03/04(日) 21:55:25大体こんな感じじゃない?
usage() {
echo "Usage: ..." 1>&2
exit 1
}
test -d $1 && find $1 -not -name '\.*' -size 0 -print || usage()
ただし補足1はGNU findの機能を使ってるので、どのfindでもということなら
$1が空だった場合の扱いを自分でする必要がある。あと、「.」で始まる
フォルダは処理したいのなら、find の条件をもうちょっと詰める必要がある。
0184名無しさん@お腹いっぱい。
2007/03/04(日) 21:59:310185名無しさん@お腹いっぱい。
2007/03/04(日) 22:01:31レスありがとうございます。
問題文は元々英語の為、変な訳になって細かい部分が伝わらず申し訳ありません。
英文ですが、元の問題はこちらです。ttp://user.ftth100.com/log/up/log/3569.gif
0186181
2007/03/04(日) 22:21:53分かりやすい解説付でありがとうございます。
この場合、
test -d $1(ディレクトリ名を引数1に格納)
&& find $1 -not -name '\.*' -size 0(ファイル名の頭文字が「.」、サイズが0の物をはじく)
-print || usage() (結果をusage()に返し、出力)
usage() で、正常ならば結果を出力。エラーならば1>&2が働きエラーを表示。そしてexitで終了。
といった感じでしょうか?
0187名無しさん@お腹いっぱい。
2007/03/04(日) 22:46:22この問題って出所はどこなの?
0189名無しさん@お腹いっぱい。
2007/03/04(日) 23:23:20>>181 のいう通りなので、後は自分で解読してみてください。
0190名無しさん@お腹いっぱい。
2007/03/04(日) 23:24:060191名無しさん@お腹いっぱい。
2007/03/05(月) 14:36:49短絡評価でぐぐれ
0192名無しさん@お腹いっぱい。
2007/03/05(月) 15:46:28sed -n '/^[yn][eo]/p;s/^IPアドレス.//p' data.txt | fmt -w 18 | tr ' ' ,
0193192
2007/03/05(月) 15:55:14yes/no 個別に書くべきですね。
それとスペースを数えると正しくは fmt -w 19 だ ww
0194192
2007/03/05(月) 16:14:27スレ汚し済まない。
0195名無しさん@お腹いっぱい。
2007/03/05(月) 16:49:38perl -ne 'BEGIN { $/ = "----\n" } /(yes|no).*(\d+(?:\.\d+){3})/s && print "$1,$2\n"'
0196名無しさん@お腹いっぱい。
2007/03/05(月) 17:46:13sed -f 135.sed
$ cat 135.sed
/^----/{N
s/^----\n//
x
d
b
}
/^IPアドレス /{
s/IPアドレス /,/
x
G
s/\n//
b
}
d
0197名無しさん@お腹いっぱい。
2007/03/07(水) 17:04:39一行ずつ一つのログファイルに延々と結果が記載されます。
100
ABC
101
200
DEF
201
…こんな感じで3行ずつが一連のアクションです。
これを以下のように整形したいのですが、妙案は有りますでしょうか?
100,ABC,101
200,DEF,201
ご教示いただければ幸いです。
0198名無しさん@お腹いっぱい。
2007/03/07(水) 17:29:18sed 'N;N;s/¥n/,/g'
む、あたまに余計な空行が付くな。
0199名無しさん@お腹いっぱい。
2007/03/07(水) 17:31:060200名無しさん@お腹いっぱい。
2007/03/07(水) 18:03:28やっぱりsedを使うのかな?と思い本を見ていたところでした。
なるほどNで行数分読み込んで、改行\nを,に置き換える…。
見れば直ぐに解りますが、これを0から考えるのは大変でした。
今度は誰かに教えれるよう、頑張ります。
素早いご解答ありがとうございました!
0201名無しさん@お腹いっぱい。
2007/03/08(木) 12:24:02利用者が指定した文字列の書いてあるファイルを読み込んで、
配列に文字列を一つずつ格納し、その文字列を順番に出力させたいのですが、
どのようにすれば良いのでしょうか?
0202名無しさん@お腹いっぱい。
2007/03/08(木) 12:32:35どのシェルよ?
少なくともピュアBourneシェルには配列はない。
0203名無しさん@お腹いっぱい。
2007/03/08(木) 12:33:26申し訳ありません。bashです。
0204名無しさん@お腹いっぱい。
2007/03/08(木) 12:37:11tr ' ' '¥012' < ユーザーが指定したファイル | sort
という意味?
指定したファイルの中はどんな構造なの?
1行に1語なのか、フリーテキストか。
0205名無しさん@お腹いっぱい。
2007/03/08(木) 12:40:27指定したファイルは、おっしゃる通り、1行1語のテキストファイルです。
Sapporo
Tokyo
Osaka
Nagoya
Fukuoka
といった感じです。
単純に中身を表示するだけなら、
echo -n "ファイル名を入力してください:"
read x
cat $x
でよいと思われるのですが、一度配列に全文字列を格納してやるので困っています…。
0206名無しさん@お腹いっぱい。
2007/03/08(木) 12:46:39sort そのファイル
とするのとは違うの?
0207名無しさん@お腹いっぱい。
2007/03/08(木) 12:55:10sort ファイル とは違い、この場合だと、
array[0]=Sapporo
array[1]=Tokyo
array[2]=Osaka
array[3]=Nagoya
array[4]=Fukuoka
i=0
while [$1 -le 5]
do
echo ${array[$i]}
let i=$i+1
done
読み込んだテキストファイルをこのような感じで出力させたいです。
0208名無しさん@お腹いっぱい。
2007/03/08(木) 12:58:490209名無しさん@お腹いっぱい。
2007/03/08(木) 13:05:51シェルスクリプトじゃなきゃいかんの?
perlかなんか使った方が楽じゃない?
0210名無しさん@お腹いっぱい。
2007/03/08(木) 13:12:43「整列させて」という意味ではないことは理解した。
0211名無しさん@お腹いっぱい。
2007/03/08(木) 13:16:34words=($words $word)
done < 指定したファイル
してあとは一緒。
0212名無しさん@お腹いっぱい。
2007/03/08(木) 13:43:20perlじゃ駄目なんです。
>>211
echo -n "ファイル名を入力してください:"
while read word; do
words=($words $word)
done < $word
という事でしょうか?
0213名無しさん@お腹いっぱい。
2007/03/08(木) 14:02:360214名無しさん@お腹いっぱい。
2007/03/08(木) 14:07:43すいません・・・
どうやれば良いのでしょうか?
0215名無しさん@お腹いっぱい。
2007/03/08(木) 14:10:26echo -n "ファイル名を入力してください:"
read source
while ....
done < $source
あとは出力のためのループ
0216名無しさん@お腹いっぱい。
2007/03/08(木) 14:52:24echo -n "ファイル名を入力してください:"
read source
while read word; do
words=($words $word)
done < $source
cat $source | while read
do
echo $words
let source=$source+1
done
でやってみましたが、テキストの最初の文字列しか表示されません。
後半の出力ループがいけないのでしょうか?
0217名無しさん@お腹いっぱい。
2007/03/08(木) 15:06:490218名無しさん@お腹いっぱい。
2007/03/08(木) 15:12:44代わりに一パラメータで逃げてみた。
while read word; do
set -- $* $word
done < $source
for word in $*; do
echo $word
done
0219名無しさん@お腹いっぱい。
2007/03/08(木) 15:18:07×一パラメータ
○位置パラメータ
あんど、こう書くらしい。
while read word; do
size=${#words[@]}
words[$size]=$word
done < $source
for word in ${words[@]}; do
echo $word
done
0220名無しさん@お腹いっぱい。
2007/03/08(木) 15:24:40↓
http://pc11.2ch.net/test/read.cgi/linux/1154578200/
ここはBourneのみでよろしこ。
0221名無しさん@お腹いっぱい。
2007/03/08(木) 15:49:17動作確認出来ました。本当にありがとうございました。
>>220
誘導ありがとうございます。
スレ違いで申し訳ありませんでした。
0222名無しさん@お腹いっぱい。
2007/03/08(木) 15:53:31教えてください
#!/bin/sh
COUNTER=0
while [ $COUNTER -lt 100 ]; do
mv "$COUNTER".jpg /home/more/
let COUNTER=COUNTER+1
done
こんな感じのとき最初の10までが1桁になってしまうのですが
01 02 03,,,,
行頭に0を付けた2桁で処理するのはどうすれば良いのでしょうか?
00-09と10-99でループをわけるしか無いのでしょうか?
■ このスレッドは過去ログ倉庫に格納されています