シェルスクリプト総合 その19
■ このスレッドは過去ログ倉庫に格納されています
0001シェルスクリプトライター
2011/12/10(土) 20:06:40.38スクリプトのお勉強・自慢・腕試しなどにどうぞ。
□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
Linuxユーザは/bin/shの正体がbashなので特に注意。
FreeBSDユーザは/bin/shの正体がashなので注意。
v7 shに一番近くて、現役のshは、OpenSolaris由来のheirloom sh。
http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/sh/
http://heirloom.sourceforge.net/sh.html
・csh/tcshのシェルスクリプトは推奨されません。
(理由は「csh-whynot」でググれ)
・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。
manや参考リンクを見ましょう。
aproposないしはman -kでそれらしい単語による簡単な検索もできます。
・シェルスクリプトのことをシェルってゆーな
・シェルで使えるワイルドカード等は正規表現ではありません。
正規表現の話題はスレ違い(正規表現スレへ)
□初心者へのアドバイス:
・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも
RubyやPerlの方が適した仕事には素直にそちらを使いましょう。
・知らないコマンドが出てきたらmanを引きましょう。
・思い通りに動かないときは、まずは sh -x でトレースしましょう。
前スレ
シェルスクリプト総合 その18
http://hibari.2ch.net/test/read.cgi/unix/1308195527/
次スレは >>970 で。
0566名無しさん@お腹いっぱい。
2012/03/16(金) 21:21:16.683/19の10:00まで待つと書きましたが、前倒して3/17の16:30までとします
>>557 にあるフォーマットでお願いします
0567名無しさん@お腹いっぱい。
2012/03/16(金) 21:22:56.00もちろんletも使えない。
0568名無しさん@お腹いっぱい。
2012/03/16(金) 21:41:50.80最大公約数求めるのに引数デクリメントしながらすべて割って試すとか
総当たりの非効率アルゴリズム使ってボケるんなら、
最小公倍数でもインクリメントしながら順番に割って総当たりするアルゴリズムで
ボケるべき。
0569名無しさん@お腹いっぱい。
2012/03/16(金) 21:49:10.39/bin/sh まで許せるんだ?
FreeBSD の ports で拡張機能すべて許可した v7sh では func(){}
すら Syntax error なんだが...
# 少なくとも ``#!/usr/bin/env bash'' くらいにしないと販促だ >>563
0570名無しさん@お腹いっぱい。
2012/03/16(金) 21:54:56.85バカ共の相手が面倒になったので締め切りました。
やっぱりバカしか居ないんだな。
0571名無しさん@お腹いっぱい。
2012/03/16(金) 22:25:57.270572名無しさん@お腹いっぱい。
2012/03/16(金) 22:27:13.29できたよ。GCMとLCMの計算方法が対称的になるようにしたよ。
/bin/sh純正だ。アルゴリズムはウケ狙いだけどな。
#!/bin/sh
GCM=$1
while :; do
for i in "$@"; do
if [ `expr "$i" % "$GCM"` != 0 ]; then
GCM=`expr "$GCM" - 1`
continue 2
fi
done
echo GCM = "$GCM"
break
done
LCM=$1
while :; do
for i in "$@"; do
if [ `expr "$LCM" % "$i"` != 0 ]; then
LCM=`expr "$LCM" + 1`
continue 2
fi
done
echo LCM = "$LCM"
break
done
0573名無しさん@お腹いっぱい。
2012/03/16(金) 22:28:07.310574名無しさん@お腹いっぱい。
2012/03/16(金) 23:17:57.603/17 17:00までに提出しないと単位が足りなくなるのが分かったので質問してます
レポートに貼り付けるのは私がしますので、3/17の16:30までにお願いします
また他の皆さんについても、ソースにバグがないかどうか、早急なチェックとデバッグを
期待しています
>>557 にあるフォーマットでお願いします
0575名無しさん@お腹いっぱい。
2012/03/16(金) 23:25:20.310576名無しさん@お腹いっぱい。
2012/03/16(金) 23:37:15.490577名無しさん@お腹いっぱい。
2012/03/16(金) 23:39:45.15変更がないファイルを編集後ディレクトリから削除する
シェルスクリプトを書こうとしています。
diffをディレクトリ同士で実行し同名ファイルを探し
その同名ファイルをdiffし返値が空なら削除という操作を
再帰的に階層を掘って実行すれば良いと思うのですが、
どうも何から書けばいいのか詰まってしまいました。
奇特な方居ましたらアドバイスお願いします。
0578名無しさん@お腹いっぱい。
2012/03/16(金) 23:45:20.44まず、man diffをちゃんと嫁
0579名無しさん@お腹いっぱい。
2012/03/17(土) 00:14:42.67オプションよく読んでいませんでした。
読んで出直してきます。ありがとうございました。
0580名無しさん@お腹いっぱい。
2012/03/17(土) 05:25:42.88目的に対する作業の方法が間違ってる気がする。
何のために「編集前ディレクトリと編集後ディレクトリを比較して、
変更がないファイルを編集後ディレクトリから削除する」のか
を書いた方が、適切な答えが帰ってくると思う。
0581名無しさん@お腹いっぱい。
2012/03/17(土) 07:12:42.07ひな形っぽいもの。階層が一つだけだからヒントにしかならんだろうけど。変数は出鱈目。
やりたいことのイメージは、こんなもんでしょ?
# mkdir /tmp/mae
# mkdir /tmp/ato
# MAE=/tmp/mae
# ATO=/tmp/ato
#
# touch $MAE/aaa
# touch $MAE/bbb
# cp -p $MAE/aaa $ATO/
# echo hoge > $ATO/bbb
#
# cd $ATO
# for ii in `ls`
> do
> [ -f $MAE/$ii ] && diff $ii $MAE > /dev/null 2>&1
> [ $? = 0 ] && rm $ii
> done
#
# ll $ATO
bbb
ちなみに HP-UX で検証。
ls > tmp.txt して while read LINE; do〜done < tmp.txt とか LINE=`head $NUMBER tmp.txt | tail -1` でも目的だけは達成できる感じ。
0583名無しさん@お腹いっぱい。
2012/03/17(土) 15:36:57.64こんなバカみたいな総当たりはダメだって言われちゃいました
もっと数学的な解法をアルゴリズム化したものでないとダメです
結果が正しければいいってレベルじゃないんです
100万とか1000万とか、それ位大きな値になると、ちっとも終わらないじゃないですか
期限は今日の16:40までとします
くれぐれもよろしくお願いします
0584名無しさん@お腹いっぱい。
2012/03/17(土) 16:12:07.29できたよ。100万でも1000万でも、正しく実行すればすぐ終るよ。
#!/bin/sh
while :; do
echo -n 'Enter GCM = '; read GCM
for i in "$@"; do
[ `expr "$i" % "$GCM"` != 0 ] && { echo 'Try again'; continue 2; }
done; break; done
echo GCM = "$GCM"
while :; do
echo -n 'Enter LCM = '; read LCM
for i in "$@"; do
[ `expr "$LCM" % "$i"` != 0 ] && { echo 'Try again'; continue 2; }
done; break; done
echo LCM = "$LCM"
0585名無しさん@お腹いっぱい。
2012/03/17(土) 22:26:32.20LANG=C diff -qrs a b の出力結果でなんとかする
というかバージョン管理システムを使うのが良い気がする
0586名無しさん@お腹いっぱい。
2012/03/19(月) 12:31:35.64B=$A
touch $B
とすると、"*.txt"というファイルが出来てしまうんですが
これ$Bを展開させたいときはどうすればいいんですかね
0587名無しさん@お腹いっぱい。
2012/03/19(月) 12:40:39.27本当に >>586 のとおりに実行したのなら *.txt は展開される。
展開されないのはカレントディレクトリに *.txt にマッチするファイルがないから。
すでにファイルがなければ展開しようがない。
0588586
2012/03/19(月) 12:56:05.81ヒントありがとうございます
oppai.txt がある場合⇒ oppai.txtにタッチ
oppai.txt がない場合⇒ *.txt を作成
という動きになりました
まーこれはこれで使い物になりませんわ
0589名無しさん@お腹いっぱい。
2012/03/19(月) 13:11:29.400590名無しさん@お腹いっぱい。
2012/03/19(月) 13:13:49.05もし「存在しなければ作成したくない」という意図なら、
touch -c $B で桶。
0591名無しさん@お腹いっぱい。
2012/03/19(月) 13:45:15.79エスパーすると、
A=*.txt
B=$A
mv $A /どこか
touch $B
みたいなことをやりたいのでは?
それなら、
A=`echo *.txt`
mv $A /どこか
touch $A
で桶。変数はAだけでよい。
0593名無しさん@お腹いっぱい。
2012/03/19(月) 14:28:02.200594名無しさん@お腹いっぱい。
2012/03/19(月) 15:43:16.300595名無しさん@お腹いっぱい。
2012/03/19(月) 15:45:13.28これなんでBをはさんでるの?
0596名無しさん@お腹いっぱい。
2012/03/19(月) 17:56:07.30Bに代入し直せば再解釈されて展開されると勘違いしたのだろう
0597名無しさん@お腹いっぱい。
2012/03/19(月) 19:53:24.010598名無しさん@お腹いっぱい。
2012/03/19(月) 19:56:34.87touch $B みたいにクォートなしで参照した時に初めて展開される。
0600名無しさん@お腹いっぱい。
2012/03/19(月) 20:04:56.950601名無しさん@お腹いっぱい。
2012/03/19(月) 20:08:20.980602名無しさん@お腹いっぱい。
2012/03/19(月) 21:12:40.160603名無しさん@お腹いっぱい。
2012/03/19(月) 22:06:26.63ttp://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h
0604名無しさん@お腹いっぱい。
2012/03/20(火) 08:01:57.14のサイトに、
#!/bin/sh
read input
if [ $input = 'q' ]; then
echo QUIT
fi
と言うシェルスクリプトを参考に
if [ "$input" = 'q' ]; then
と $input をダブルクォートで囲めばよい。しかしまだ落とし穴はある。たとえば "!" を入力すると、
[: =: unexpected operator
とまたしてもエラーになってしまう。これは "$input" が "!" であるため、
if [ ! = 'q' ]; then
として扱われたからだ。
ってありますけれど、何度!を入力しても、
[: =: unexpected operator
って表示されません。
それに、
$ test !c = 'q' ; echo $?
test cd /etc = 'q' ; echo $?
test: too many arguments
とエラーが表示されます。
どのような解釈をすると、!cがtest cd /etcに置換されるのでしょうか?
0605名無しさん@お腹いっぱい。
2012/03/20(火) 09:04:17.23これはそのように解釈するtestもあるから注意しろ。くらいでいいと思う。
> どのような解釈をすると、!cがtest cd /etcに置換されるのでしょうか?
ヒストリ置換だろ。
0606名無しさん@お腹いっぱい。
2012/03/20(火) 17:36:56.09今の時代、そのエラーが出るshを使っている人、いるのかね?
0607名無しさん@お腹いっぱい。
2012/03/20(火) 17:41:15.64[ ! = ! ]
[ [ = [ ]
[ ] = ] ]
[ = = = ]
[ -f = -f ]
など、すべて文字列の比較として期待通りに動作する
0608名無しさん@お腹いっぱい。
2012/03/20(火) 17:51:22.47#!/bin/bashとか書いてるのに
0609名無しさん@お腹いっぱい。
2012/03/20(火) 19:57:58.150610名無しさん@お腹いっぱい。
2012/03/20(火) 21:45:56.600611名無しさん@お腹いっぱい。
2012/03/20(火) 23:34:08.600612名無しさん@お腹いっぱい。
2012/03/21(水) 09:04:25.40どうインジェクションするか解説お願いします
0613名無しさん@お腹いっぱい。
2012/03/21(水) 10:08:32.370614名無しさん@お腹いっぱい。
2012/03/21(水) 10:47:17.62case "$hoge" in
yes) ...;;
esac
って書くよね、ふつー。[Yy][Ee][Ss] なんかに変えるのも簡単だし。
0615名無しさん@お腹いっぱい。
2012/03/21(水) 11:14:27.91>>610はダブルクオートで囲ってるから、[コマンドの第一引数として$hogeの中身が渡るんで、そのhogeの値はそのまま[コマンドが解釈する。
どうあがいてもインジェクションなんて無理だと思うんだが。
0616名無しさん@お腹いっぱい。
2012/03/21(水) 11:26:14.79もうカンベンしてやれ。
0617名無しさん@お腹いっぱい。
2012/03/21(水) 12:19:06.390618名無しさん@お腹いっぱい。
2012/03/21(水) 13:26:55.780619名無しさん@お腹いっぱい。
2012/03/21(水) 13:38:09.350620名無しさん@お腹いっぱい。
2012/03/21(水) 14:28:05.05座布団
0621名無しさん@お腹いっぱい。
2012/03/21(水) 15:37:01.150622名無しさん@お腹いっぱい。
2012/03/21(水) 19:24:26.73$ busybox ash
$ hoge=!
$ if [ "$hoge" = 'q' ]; then echo QUIT; fi
ash: q: unknown operand
0623名無しさん@お腹いっぱい。
2012/03/21(水) 22:56:44.16に
>シェルスクリプトに制御構文が増えることを極力避けるように様々な工夫をしている。
>制御構文を避ける理由はコードが読みにくくなるためだ。
>これまで制御構文whileやforのはずし方について書いてきた。
>処理速度を高速化するためにwhileやforをコマンドに置き換えるというのは、
>それなりの効果が期待できる方法だ。
>シェルスクリプトに制御構文であるforやwhileが出てきたら、
>何か避ける方法がないか探してみるとより高速なスクリプトが書けるようになるかもしれない。
ってかいてあって、いろんな例が書いてあったり、
変なパッケージ(python製)を入れて、そのコマンドを使ったりしてるんですね。
僕は制御構文外すと逆に可読性がおちたり、
pythonで書かれたコマンドを呼び出すより、シェルスクリプトで制御構文書いた方が
早いと思うんですけど、おかしいですかね?
0624名無しさん@お腹いっぱい。
2012/03/21(水) 23:01:45.31以下のようなファイルがあるとします。
-----------------
1. aaa hello
2. bbb
3. ccc
4. ddd hello
5. ddd hello
6. eee hello
-----------------
このファイルの2行目から5行目に限定して、helloをgood byに変更したいです。
どのようなやりかたがありますでしょうか?
sedを使えばいいのかなと思うのですが。。
0625名無しさん@お腹いっぱい。
2012/03/21(水) 23:09:05.420626名無しさん@お腹いっぱい。
2012/03/21(水) 23:34:20.22うん、sedの超基本的な使い方で出来る
0627名無しさん@お腹いっぱい。
2012/03/21(水) 23:34:53.130628名無しさん@お腹いっぱい。
2012/03/21(水) 23:41:30.82そのページを見て思ったことは、xargsは便利だね、くらい
0629名無しさん@お腹いっぱい。
2012/03/22(木) 00:58:20.98シェルスクリプトに限らず、プログラム書く時に(一部の)制御構文を使わないってのは個人的に良くやる。効率とか性能とか抜きで。
ゲームの縛りプレイみたいなもんだけど、意外なテクニックを発見したりできて楽しめるよ。おすすめ。
上司や同僚に見つかったら>>623のサイトみたいな適当なウンチクで誤魔化せばOK。
0630名無しさん@お腹いっぱい。
2012/03/22(木) 01:14:19.58sed で 2行目から5行目は 2,5。
hello を good byに置換するには s/hello/good by/
接続すると 2,5s/hello/good by/
0631名無しさん@お腹いっぱい。
2012/03/22(木) 02:23:18.01BASICのころはあったけどな。
if文分岐とかで速度差が出ないようにするとかで。
今はそういうのする必要ないけど。
0632名無しさん@お腹いっぱい。
2012/03/22(木) 08:22:33.32感じるものさ
0633名無しさん@お腹いっぱい。
2012/03/22(木) 08:28:41.720634名無しさん@お腹いっぱい。
2012/03/22(木) 09:18:49.180635名無しさん@お腹いっぱい。
2012/03/22(木) 09:56:14.040636名無しさん@お腹いっぱい。
2012/03/22(木) 11:24:16.01基本動作はキューなんですが、
1 2 3
↓1を実行
2 3
1 2 3
↓2を実行
1 3
としたいです
unset 配列[N]でインデックスを詰めてくれればこんなの楽勝なのに…
0637名無しさん@お腹いっぱい。
2012/03/22(木) 11:37:02.12ファイルにしたらいい
0638名無しさん@お腹いっぱい。
2012/03/22(木) 20:00:10.46$ set -- 1 2 3 4
$ shift
$ echo $@
2 3 4
$ set -- 1 2 3 4
$ set -- $1 ${@:3}
$ echo $@
1 3 4
0639名無しさん@お腹いっぱい。
2012/03/22(木) 20:22:13.25unset array[N] で要素をunsetした後に、
array=(${array[@]}) でセットしなおせばインデックスは詰まる。
>>638 とは違って位置パラメータを壊さずに済む。
0640名無しさん@お腹いっぱい。
2012/03/22(木) 21:29:35.34かっこういい
0641名無しさん@お腹いっぱい。
2012/03/23(金) 22:05:53.20・ファイルを指定フォルダ内へコピー
・同名ファイルが存在する場合は、
既に存在するファイル名を「ファイル名 (1)」へ変更し、
既に「ファイル名 (1)」が存在しているのであれば、
それをさらに「ファイル名 (2)」へ変更し、、、(繰り返し)
というシェルスクリプトを書きたいんですが、
既に「ファイル名 (*)」が存在する場合に*の数字を
どのようにすれば知ることができますか?
*さえ知れたらexprを使えば出来るとは思うのですが...
アドバイスお願いします。
0642名無しさん@お腹いっぱい。
2012/03/23(金) 22:27:01.94逆に考える。
変数iとかに1を入れておいて、
"ファイル名($i)" が存在したら
iをインクリメントして "ファイル名($i)" にリネームする。
0643名無しさん@お腹いっぱい。
2012/03/23(金) 22:28:54.510644名無しさん@お腹いっぱい。
2012/03/23(金) 22:32:05.36i=1; while [ -f "file($i).txt" ]; do i=`expr $i + 1`; done; echo $i
0645名無しさん@お腹いっぱい。
2012/03/23(金) 22:52:22.200646名無しさん@お腹いっぱい。
2012/03/23(金) 22:56:33.590647名無しさん@お腹いっぱい。
2012/03/24(土) 01:18:46.160648名無しさん@お腹いっぱい。
2012/03/24(土) 10:30:46.53つまりアヌスを知りたいと……教えてあげよう
0649名無しさん@お腹いっぱい。
2012/03/24(土) 18:23:06.74実機が無いので試せないけど・・・
第一引数:コピー対象ファイル名(絶対パス指定)
第二引数:コピー先ディレクトリパス
#!/bin/ksh
FILE_NAMEW=`basename ${1}`
COUNT=1
TARGET_FILE=${2}/${FILE_NAME}
if [[ -e ${TARGET_FILE} ]]; then
while true
do
if [[ -e "${TARGET_FILE}(${COUNT})" ]]; then
COUNT=`expr ${COUNT} + 1`
else
cp ${1} ${TARGET_FILE}(${COUNT})
break
fi
done
else
cp ${1} ${TARGET_FILE}
fi
exit 0
0650名無しさん@お腹いっぱい。
2012/03/24(土) 18:40:54.370651名無しさん@お腹いっぱい。
2012/03/24(土) 18:48:18.47第1引数: コピー元ファイル(絶対/相対path関係なし)
第2引数: コピー先ファイル(ディレクトリではない)
#!/bin/sh
FILE=$2
if [ -f "$FILE" ]; then
i=1
while [ -f "$FILE($i)" ]; do
i=`expr $i + 1`
done
FILE="$FILE($i)"
fi
cp "$1" "$FILE"
0652641
2012/03/24(土) 21:11:20.76642さんの意見を参考に書いていたら、
651さんとほぼ同じようなものが書けました。
勉強になりました!
0653名無しさん@お腹いっぱい。
2012/03/24(土) 22:20:08.70みんなも勉強になるんだけどなぁ
0654名無しさん@お腹いっぱい。
2012/03/24(土) 22:34:45.96[ -e "$2" ]&&{ i=0; while [ -e "$2($((++i)))" ];do :;done; set "$1" "$2($i)";}
cp "$1" "$2"
0655名無しさん@お腹いっぱい。
2012/03/25(日) 10:39:31.72ってどういう風に解釈すれば良いんですか?
-- って正規表現ですか?
*はファイル名ですか?
0656名無しさん@お腹いっぱい。
2012/03/25(日) 10:59:50.45その * は glob。シェルが解釈する。
シェルが展開して rm に渡す。
-- は正規表現でも何でもなくて、ただの --。
シェルは特に何もしない。
そのまま rm に渡される。
0657名無しさん@お腹いっぱい。
2012/03/25(日) 11:00:45.800658名無しさん@お腹いっぱい。
2012/03/25(日) 11:28:39.15マッチしたときのための策だな。
0659名無しさん@お腹いっぱい。
2012/03/25(日) 11:51:49.520660名無しさん@お腹いっぱい。
2012/03/25(日) 12:14:56.30調べたらBSD系はgetopt(3)つこうてるから--大丈夫だよと書いてあったんだけど
他のUNIXはまた違うの?
0661名無しさん@お腹いっぱい。
2012/03/25(日) 12:24:48.030662名無しさん@お腹いっぱい。
2012/03/25(日) 13:38:40.680663名無しさん@お腹いっぱい。
2012/03/25(日) 13:56:37.10将来は -- が使えなくなるので注意、と書いてあるね。
0664名無しさん@お腹いっぱい。
2012/03/25(日) 18:55:04.62rm ./*
みたいにすれば引数の先頭が - になることはない。
--が使える保証がなければこっちで。
0665名無しさん@お腹いっぱい。
2012/03/25(日) 20:43:30.34で、-- が使えない ln で、-sという名前を指しているsymlinkを作ろうと、
ln -s ./-s hoge ってやると、hoge -> ./-s というsymlinkができて、
symlink自体に ./ が含まれてしまって美しくない。
-- が使えない条件で 、hoge -> -s は作れないものか。
■ このスレッドは過去ログ倉庫に格納されています