シェルスクリプト総合 その9
■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。
2007/08/15(水) 07:25:02スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>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 でトレースしましょう。
前スレ
シェルスクリプト総合 その8
http://pc11.2ch.net/test/read.cgi/unix/1171517324/
0275名無しさん@お腹いっぱい。
2007/10/05(金) 09:09:240276名無しさん@お腹いっぱい。
2007/10/05(金) 15:53:060277名無しさん@お腹いっぱい。
2007/10/07(日) 15:08:28a=hoge
b=a
こういう時
echo "$b"
↓b=aなので
echo "$a"
↓a=hogeなので
hoge
みたいな処理をしたいのですができますでしょうか?
0278名無しさん@お腹いっぱい。
2007/10/07(日) 15:21:340279名無しさん@お腹いっぱい。
2007/10/08(月) 05:23:270280277
2007/10/08(月) 06:08:15変数の中に変数を入れることはできないでしょうか?
自分の応用力の無さが嫌になります
hoge_A=AAA
foo=A
${hoge_$foo}
↓foo=Aなので
${hoge_A}
↓
AAA
0281名無しさん@お腹いっぱい。
2007/10/08(月) 06:29:01eval "echo \"\${hoge_$foo}\"
>>277 ならbash 限定かもしれんが eval なしでもできる
echo "${!b}"
0282名無しさん@お腹いっぱい。
2007/10/08(月) 08:02:57eval echo \"\$hoge_$foo\"
変数の中に空白とかがなければ
eval echo \$hoge_$foo
でも桶。
>>281 のひとつ目の解答、ダブルクォートがおかしいよ。
0283名無しさん@お腹いっぱい。
2007/10/08(月) 20:38:58>>278は
入力: eval echo \$$b
↓ 変数展開($b)・エスケープ解釈
シェルが実行する文: eval echo $a
↓ そのevalコマンドを実行……
入力: echo $a ←←※
↓ 変数展開($a)
echo hoge
evalを使う式を作る場合は欲しい文※からスタートしてさかのぼればいい。
echo "$hoge_A" ←←※実行させたいケースの例
↓ Aを変数fooの参照に戻す・$などをエスケープ
echo \"\$hoge_$foo\"
↓ 頭にevalをつける
eval echo \"\$hoge_$foo\"
0284277
2007/10/08(月) 21:08:00283さんの解説を見つついろいろ試して理解しようと思います。
0285名無しさん@お腹いっぱい。
2007/10/10(水) 02:28:09echo 'aaa bbb aaa ccc" | sort
0286名無しさん@お腹いっぱい。
2007/10/10(水) 02:53:480287名無しさん@お腹いっぱい。
2007/10/13(土) 10:12:10sedでやる(置換して戻す二度手間が少しダサい)
echo "aaa bbb aaa ccc" | sed 's/ /\
/g' | sort | sed ':loop
N;s/\n/ /;$!b loop'
awkでやる(長い。一目見て何をしているのか解らない)
※とりあえず「行が長すぎる」と投稿時に怒られたので、分割しますたが元は一行です。
echo "aaa bbb aaa ccc" | \
awk '{ sizeOfArray = split($0,words," ")
for (i = 2; i <= sizeOfArray; ++i) {
for (j = i;(j-1) in words && wrods[j] < words[j-1]; --j) {
tmp = words[j]; words[j] = words[j-1]; words[j-1] = tmp
}
}
for (i = 1; i < sizeOfarray; ++i) printf("%s ",words[i])
print words[i]
}'
sortアルゴリズムを工夫すると更に訳解らん一行コマンドになりそうw
0288名無しさん@お腹いっぱい。
2007/10/13(土) 10:50:03s/\n/ /g'
0289名無しさん@お腹いっぱい。
2007/10/13(土) 11:26:440290名無しさん@お腹いっぱい。
2007/10/13(土) 11:43:340291名無しさん@お腹いっぱい。
2007/10/13(土) 12:39:03ただし、awkを使う場合はawkだけにしてsedは使わないのが美しい。
0292名無しさん@お腹いっぱい。
2007/10/13(土) 14:49:020293287
2007/10/13(土) 15:27:47済みません。perl 知らないんですw
シェルスクリプトや sed,awk なんかも勉強し始めたばかりで、
もう少し上達したら、次は perl を学ぼうと思ってます。
0294名無しさん@お腹いっぱい。
2007/10/13(土) 15:53:04ああいうの、まわりに迷惑かけてるよなぁ。
0295名無しさん@お腹いっぱい。
2007/10/13(土) 17:32:280296名無しさん@お腹いっぱい。
2007/10/13(土) 17:44:540297名無しさん@お腹いっぱい。
2007/10/13(土) 17:47:48perl -ne 'print join(" ", sort(split(/\s+/))), "\n"'
0298名無しさん@お腹いっぱい。
2007/10/13(土) 17:49:04でた、口だけ番長。
0299名無しさん@お腹いっぱい。
2007/10/13(土) 17:51:200301名無しさん@お腹いっぱい。
2007/10/13(土) 19:08:530302名無しさん@お腹いっぱい。
2007/10/13(土) 20:56:42「シェルをかく」と大して変わらん。
シェルはシェル、スクリプトはスクリプト。
0303名無しさん@お腹いっぱい。
2007/10/13(土) 21:15:30日本語大丈夫ですかw
「シェルを書く」は間違い。
「シェルで書く」は桶。
「シェルで書く」→「シェルで記述する」
→「シェルをインタプリタとして(コマンドを)記述する」
逆に、
「シェルスクリプトで書く」は間違い。
書かれたもののことを「シェルスクリプト」と言うのだから、
「シェルスクリプトで書く」だと、
「(別の)シェルスクリプトを自動的に書いてくれるようなシェルスクリプトを使う」
みたいな意味になってしまう。
0304名無しさん@お腹いっぱい。
2007/10/13(土) 21:20:49もしかすると世の中にはシェルスクリプトで書かれたエディタがあったりして
……と書いてこの「シェルスクリプトで」はあってるのか間違ってるのか
わからなくなってきた
0305名無しさん@お腹いっぱい。
2007/10/13(土) 21:25:480306名無しさん@お腹いっぱい。
2007/10/13(土) 21:46:23「てにをは」が通用しないケースが有るだけのこと。
くだくだしく文法を語らなくてよろし。
0307名無しさん@お腹いっぱい。
2007/10/15(月) 00:22:28やっぱり逃げたな。口だけ番長。
0308名無しさん@お腹いっぱい。
2007/10/15(月) 09:31:23俺はシェルで書けば直ぐ終わるものをJavaしか
書けないから?と言う理由で全部Javaで書いて
帰って行った外注さん知ってるぞ。
0309名無しさん@お腹いっぱい。
2007/10/15(月) 15:27:51chmod(相当)だって、やっと1.6で出来るようになったのに。
0310名無しさん@お腹いっぱい。
2007/10/15(月) 20:14:03権限関連の操作と、ファイルのコピー、移動は、javaから子プロセス起こしてシェルスクリプト実行してる。
1.3,1.4の環境だと実装が面倒&パフォーマンス悪すぎなので。
0311名無しさん@お腹いっぱい。
2007/10/17(水) 01:48:54#!/bin/sh -
とした方が安全、みたいなことを読んだことがあるような
気がするんですが、そういう話ってありますか?
なんかごにょごにょして予期しないオプションを渡されないように、
みたいなことだったと思うんですが。
0312名無しさん@お腹いっぱい。
2007/10/17(水) 06:03:25とりあえず、ファイルのフルパスのみ、もしくは、ファイル名のみであれば以下で表示できるのですが、
find `/bin/pwd` -type f -print -exec ls {} \;|xargs -i dirname {}
find `/bin/pwd` -type f -print -exec ls {} \;|xargs -i basename {}
これを同時に行って
/hoge/hoge bar.txt
/hoge/hoge foo.txt
/hoge/hoge/hoge foo.txt
のように表示することはできないでしょうか?
あるコマンドの処理結果の一行に対して複数コマンドを発行する、ということになると思うのですが。
一旦 set -A で配列に入れてからloopで一行づつ処理することも考えたのですが、ファイル数が多すぎる場合Errorになってしまいます。
どなたかご教示ください
0313名無しさん@お腹いっぱい。
2007/10/17(水) 09:05:20話はそれからだ。
0314名無しさん@お腹いっぱい。
2007/10/17(水) 09:12:11でどうかな。
0315名無しさん@お腹いっぱい。
2007/10/17(水) 09:12:53適当に直してください。
0316名無しさん@お腹いっぱい。
2007/10/17(水) 09:14:22できたよ。
find `/bin/pwd` -type f -exec sh -c 'echo `dirname {}; basename {}`' \;
0317名無しさん@お腹いっぱい。
2007/10/17(水) 13:53:01> s/.../.../g は GNU 拡張で s|...|...|g にしたから、/ にする所は
それってGNU拡張?
雉も鳴かずば撃たれまいに……w
0318名無しさん@お腹いっぱい。
2007/10/18(木) 01:54:00うぉ、実はedからこういう挙動なのね:
$ echo hogehoge > a
$ ed a
9
,s,hoge,fuga,g
w
9
q
$ cat a
fugafuga
なんとなくPerlとかそういう無節操な時代になってからの事だとばかり・・・(恥
0319名無しさん@お腹いっぱい。
2007/10/18(木) 19:51:23find "`/bin/pwd`" -type f -exec sh -c "echo \`dirname '{}';basename '{}'\`" \;
または312のようにそれぞれ一方だけを出力する方法はわかるのなら
それぞれ出力して後からくっつけるとかでもいいし
find 〜 -exec dirname '{}' \; > /tmp/dirname.txt
find 〜 -exec basename '{}' \; > /tmp/basename.txt
paste -d ' ' /tmp/dirname.txt /tmp/basename.txt
もしくはリストを出力させてwhileループで料理してもいい
find 〜 -print | while read f; do
echo "`dirname $f` `basename $f`"
done
他にはfindに-execを複数並べて、後から2行を1行にまとめるのでも。
find 〜 -exec dirname '{}' \; -exec basename '{}' \; | while read d; do read b; echo "$d $b"; done
0320名無しさん@お腹いっぱい。
2007/10/18(木) 19:55:240321名無しさん@お腹いっぱい。
2007/10/20(土) 08:46:44先頭に#があり、行の終わりにキーワード(Japan_A)がある行
から次の#と行の終わりにキーワード(Japan_X)がある直前まで読み込む
方法を考えています。
readとwhile文で簡単に実現するにはどうすれば、よいでしょうか。
while の条件がよくからないです。
====データ構造
BBB nnn
AAAA
#△△ABC△△△△Japan_A ← このパターンを検出して、次の行を読み込む。
:
123△△456
:
:△789△10
:
#△△ABC△△△△Japan_Z ← ここのひとつ前の行まで、
:zzz△12
:
====
0322名無しさん@お腹いっぱい。
2007/10/20(土) 09:58:25whileの条件: コマンドの終了ステータスが0ならループ
その例はsedでやるのが簡単で速いけど。
0323名無しさん@お腹いっぱい。
2007/10/20(土) 12:09:46変数に代入するのってどうやればいいでしょうか?
0324名無しさん@お腹いっぱい。
2007/10/20(土) 12:30:470325名無しさん@お腹いっぱい。
2007/10/20(土) 13:24:38で変数numが1〜10の値の場合、
という処理をしたい場合、
下記って正式にサポートされている構文なんでしょうか。
一応動きますが。。。
if [ ${num} -ge 0 ] && [ ${num} -le 9 ]
また、${num}が1〜9の値の場合、
echo "${num} is 1-9."
というコマンドを実行したい場合、
下記のような記述の仕方も認められているんでしょうか?
if [ ${num} -ge 0 ] && [ ${num} -le 9 ] && echo "${num} is 1-9."
0326名無しさん@お腹いっぱい。
2007/10/20(土) 14:10:17「正式サポート」って、Bourne shell桶っていう意味かな?
であればすべて桶。無問題。
一番下の行、間違ってるよ。ifは要らん。
× if [ ${num} -ge 0 ] && [ ${num} -le 9 ] && echo "${num} is 1-9."
○ [ ${num} -ge 0 ] && [ ${num} -le 9 ] && echo "${num} is 1-9."
0327名無しさん@お腹いっぱい。
2007/10/20(土) 14:40:32if文の条件式のところに書くのは「リスト」
リストというのは、1つ以上のコマンド(のパイプ列)を&&や||でつないだもの
[ ${num} -ge 0 ] && [ ${num} -le 9 ] がリストとして正当な以上、
if文の条件としても正当。
0328名無しさん@お腹いっぱい。
2007/10/20(土) 15:04:29if文の中の&&が正式サポートされているという事ですが、
if文がないのに、条件式だけ&&で書いても実行できてしまうのは
正式サポートなんですか?
0329名無しさん@お腹いっぱい。
2007/10/20(土) 15:20:55それがリスト。
&&の機能は、左のコマンドが成功したら(0を返したら)右のコマンドを実行する。
||の機能は、左のコマンドが失敗したら(非0を返したら)右のコマンドを実行する。
他に ; てのもあって、これは左のコマンドを実行し、続いて右のコマンドを実行する。
0330名無しさん@お腹いっぱい。
2007/10/20(土) 15:36:38if [ ${num} -ge 0 ] && [ ${num} -le 9 ]; then
↓
if [ ${num} -ge 0 -a ${num} -le 9 ]; then
今時はないが、[ ] が外部コマンドになってるシェルの場合、
下の方法の方がtestが1回で済むため速い。
あと、変数numが空の場合にtestが混乱するのを避けるため、
常にダブルクォートを付ける癖を付けた方がいい。
さらに、単純な変数参照では { } は不要。
以上を考慮すると、
↓
if [ "$num" -ge 0 -a "$num" -le 9 ]; then
となる。
ところが、実はこの場合はif文じゃなくてcase文で書いた方が簡単。
すべて内部コマンドになり、少しだけ速い。
case $num in [0-9]) 実行したいコマンド;; esac
0331名無しさん@お腹いっぱい。
2007/10/20(土) 15:36:53具体的には、
&&は、左のコマンドが失敗したらそのステータス値を返して終了。
成功なら、まだ終わらず、右のコマンドの結果をステータス値を返す。
||は、左のコマンドが成功したら0で終了。
失敗なら、右のコマンドのステータスを返す。
となる。これ自体はシェルの正式な動作。
で、if 条件式…だと思っているものは正しくは if リスト ... であり、
リストを実行し、成功なら(0を返したら) then以下を〜という構文。
testというのはいろんな条件を調べて、成り立つなら成功(0)で終了、
成り立たないなら0以外を返すコマンド。if文に書くときにカッコに
見えるように [ という同機能のコマンドも用意されている。([という
名前で実行する場合は、カッコのペアに見えるように最後に ] という
引数を付ける。
0332名無しさん@お腹いっぱい。
2007/10/20(土) 22:51:51支援ありがとうございました。
sed ですか。あまり、熟知していないなぁ。
0333名無しさん@お腹いっぱい。
2007/10/21(日) 01:48:28データが収められているのが file とするとこんな感じ。
flag='false'
while read line ; do
if echo "$line" | grep -q '^#.*Japan_A' ; then
flag='true'
elif echo "$line" | grep -q '^#.*Japan_Z' ; then
flag='false'
elif $flag ; then
echo "$line"
fi
done < file
あまり効率の良いコードじゃないので、これは叩き台として使ってくれ。
0334名無しさん@お腹いっぱい。
2007/10/21(日) 05:53:04FILE[0]=a
FILE[1]=b
FILE[2]=c
この配列をforを使ってechoしたいのですがどうしたらいいでしょうか?
0335334
2007/10/21(日) 06:04:470336名無しさん@お腹いっぱい。
2007/10/21(日) 06:34:27親切な、詳細コードありがとうございます。感謝いたします。
参考にさせていただきます。
0337名無しさん@お腹いっぱい。
2007/10/22(月) 23:18:56grep の検索キーを変数などで、変更可能でしょうか。
例: 1022の部分を変数などで変更する方法?は
grep 'ABC_071022' $line
↓
grep 'ABC_???' $line
0338名無しさん@お腹いっぱい。
2007/10/22(月) 23:39:570339名無しさん@お腹いっぱい。
2007/10/22(月) 23:53:19ありがとうございます。試してみます。
( ' → " になるのですね。)
0340名無しさん@お腹いっぱい。
2007/10/23(火) 00:00:32基本から勉強しなおしてみたら?
0341名無しさん@お腹いっぱい。
2007/10/23(火) 00:05:24ご指摘のとおりかと思います。やはり基本からですね。
0342名無しさん@お腹いっぱい。
2007/10/23(火) 22:28:140343312
2007/10/25(木) 01:22:35御礼が遅くなりました。すいません。
>>314
ある出力に対して二回処理を行うのではなくて、出力自体を加工するってことですね。
問題なく実行できました。
ありがとうございます。
>316, 319
find `/bin/pwd` -type f -exec sh -c 'echo `dirname {}; basename {}`' \;
find "`/bin/pwd`" -type f -exec sh -c "echo \`dirname '{}';basename '{}'\`" \;
これ両方うまくいかないんですよね。
どうなるかというと. {} って出力がfindの結果数分続きます。
dirname, filename の引数を{}として実行するとそれぞれ "."と"{}"になるので、
{}にfindの結果が入っていないような動きだと思うんですが原因が分かりません。
よく分からなかったので試しに実験してみたところ、
find `/bin/pwd` -type f -exec echo {} \;
の出力はファイル名が出力されるのに、
find `/bin/pwd` -type f -exec sh -c echo {} \;
だとfindの結果行数分の改行(Blank行が延々続く)になってしまいます。
{}が単独で置かれていなければならないような findもある、
というmanの記述を見つけたのですが、今回のケースがそれなんでしょうか?
http://www.linux.or.jp/JM/html/GNU_findutils/man1/find.1.html
いずれにせよ環境が問題な気もするんですが、どなたか回避策ご存じないですか?
ちなみにOSはAIXです。
0344名無しさん@お腹いっぱい。
2007/10/25(木) 04:17:32多分 find じゃなくて最後の sh に与える引数のクォートが
上手くいってないんじゃないかな。
これならどうよ?
find `/bin/pwd` -type f -exec sh -c "echo {}" \;
find `/bin/pwd` -type f -exec sh -c "echo \`dirname \"{}\";basename \"{}\"\`" \;
0345名無しさん@お腹いっぱい。
2007/10/25(木) 12:12:46AIXスレでそういう特有の事情がないか聞いてみるとか。
0347名無しさん@お腹いっぱい。
2007/10/26(金) 10:20:35ここはうに板だ。
GNUでしかできないことを書き込むときはきちんと断りを入れよう。
0348名無しさん@お腹いっぱい。
2007/10/26(金) 10:55:33GNUじゃないよ。FreeBSDのfindでもうまくいく。多分、AIXのfindの問題。
0349名無しさん@お腹いっぱい。
2007/10/26(金) 11:09:58ちなみに3大商用Unixでは、どれもできなかった。
0350名無しさん@お腹いっぱい。
2007/10/26(金) 13:35:300351名無しさん@お腹いっぱい。
2007/10/26(金) 14:10:33業務用とかでも全く相手にされないオモチャなのにw
0352名無しさん@お腹いっぱい。
2007/10/26(金) 15:38:530353名無しさん@お腹いっぱい。
2007/10/26(金) 16:38:04ktkr
0354名無しさん@お腹いっぱい。
2007/10/27(土) 00:02:490355名無しさん@お腹いっぱい。
2007/10/27(土) 09:21:53getopts でのオプション解析の使用方法は下記でよいでしょうか。
comand.sh - /etc/passwd このケースの場合は、固まるような。
例:
comand.sh -p /etc/passwd
-- オプション解析部
while getopts p: option
do
case $option in
p)
AA='a'
;;
?)
echo "usage: xxxxxxxxxx"
exit 1
;;
esac
done
0356名無しさん@お腹いっぱい。
2007/10/27(土) 10:01:40case /etc/passwd in p) echo p;; *) echo '*';;esac
0357名無しさん@お腹いっぱい。
2007/10/27(土) 10:10:45それで合ってるよ。comand.sh - /etc/passwd の場合でも別に固まらない。
>>356 は何をどう勘違いしたのか知らんが、全く頓珍漢。→ 無視で桶。
0358名無しさん@お腹いっぱい。
2007/10/27(土) 10:23:06どうもご支援ありがとうございます。安心しました。
0359名無しさん@お腹いっぱい。
2007/10/27(土) 23:23:12確か case の ? は任意の一文字だから
? 自体にマッチさせたいのならエスケープが要るよ。
0360名無しさん@お腹いっぱい。
2007/10/28(日) 23:57:31といったことがしたいのですが、方法はありますでしょうか。
0361名無しさん@お腹いっぱい。
2007/10/29(月) 00:33:240362名無しさん@お腹いっぱい。
2007/10/29(月) 02:37:16% (cmd 2>&1 >&3 | tee /dev/tty) > /tmp/out 3>&1
または/dev/fd/*があるなら
% (cmd 2>&1 >&3 | tee /dev/fd/3) 3>/tmp/out
zshなら少し横着できて
% (cmd 2>&1 2>&3 >&3) 3>/tmp/out
ただし、ファイルへの出力で標準出力と標準エラー出力に別々に出力されたものの
順序を保存するのは無理。
理由は、標準エラー出力の分岐が必要だけど、それはteeのようなプロセスを
介さずにはできないから。
zshではさも分岐できるように書けるけど、裏でzshがteeの役をやっている。
0363名無しさん@お腹いっぱい。
2007/10/29(月) 07:44:29>zshなら少し横着できて
↑より上の行は zshじゃないんだよね?
だったら %のプロンプト使うなよ。csh系は 2>&1 とかの文法使えない糞シェルだから。
0364名無しさん@お腹いっぱい。
2007/10/29(月) 14:12:27ネットマスク255.255.255.0を
CIDR表記192.168.1.0/24のように置換したいのですがどうしたらいいでしょうか?
0365名無しさん@お腹いっぱい。
2007/10/29(月) 14:16:39http://pc11.2ch.net/test/read.cgi/linux/1193299060/152
http://pc11.2ch.net/test/read.cgi/linux/1136593433/452
0366名無しさん@お腹いっぱい。
2007/10/29(月) 14:17:260368名無しさん@お腹いっぱい。
2007/10/29(月) 14:37:470369364
2007/10/29(月) 14:47:41シェルスクリプトはちょっとだけわかります。
シェルスクリプトで教えてください。
0370名無しさん@お腹いっぱい。
2007/10/29(月) 14:51:410371364
2007/10/29(月) 15:00:210372名無しさん@お腹いっぱい。
2007/10/29(月) 15:19:380373名無しさん@お腹いっぱい。
2007/10/29(月) 15:21:57男子ですか?
女子なら考えますよ
0374364
2007/10/29(月) 15:25:070375名無しさん@お腹いっぱい。
2007/10/29(月) 16:18:15awk -F. '
BEGIN {
tbl[255] = 8; tbl[254] = 7; tbl[252] = 6; tbl[248] = 5;
tbl[240] = 4; tbl[224] = 3; tbl[192] = 2; tbl[128] = 1;
tbl[0] = 0
}
/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {
result = 0
for (i = 1; i <= 4; ++i) {
if (!($i in tbl))
next
result += tbl[$i]
}
print result
}'
■ このスレッドは過去ログ倉庫に格納されています