シェルスクリプト総合 その29
■ このスレッドは過去ログ倉庫に格納されています
!extend:on:vvvvv:1000:512 !extend:on:vvvvv:1000:512 シェルスクリプトに関する総合スレッドです。 スレ立て時は以下の文を先頭行に加えて下さい。 後のつけ忘れ防止の為に複数行重ねて追加推奨 !extend:on:vvvvv:1000:512 全般 ・荒しは無視しましょう。 ・丁寧な姿勢を心掛けましょう。 ・ネチケット(死語)を意識しましょう。 前スレ シェルスクリプト総合 その28 http://mevius.5ch.net/test/read.cgi/tech/1532397676/ VIPQ2_EXTDAT: default:vvvvv:1000:512:----: EXT was configured >>191 やっぱ内容には全く触れず 叩けそうな所を叩くだけなんだなw 技術者としてつまらないね 実用的じゃないもの書いてるうちは似非の技術者じゃの >>190 シェルスクリプトはストリーミング指向だから そもそも制御文を多用することは避けたほうがいいのではってのは違うのかな。 >>195 ストリーミング指向と制御文はあまり関係ないと思うよ 流れてくるものに対してなにか処理をしたいなら必ず 制御文を使うことになるし ストリーミング指向の話をするなら、関数呼び出しし結果の標準出力を 変数にキャプチャするっていうのはストリーミング指向と反してると思う 例えば result=$(command "$data") みたいな形のことね 俺もこの書き方はなるべく避けるようになった ストリーミング指向的には echo "$data" | command | 別のなにか とするほうが良いだろう ただ気づいたのがこの形だとサブシェルになってしまうから、 commandや別のなにかから、呼び出しの起点にデータを戻すことが 簡単にはできないという所。値を戻すこと自体がストリーミングの流れに反しているし かと言ってそれが必要な場合もある。例えば処理した行数をカウントしたりとかね。 なので最近はイベントハンドラ方式を使うようになった echo "$data" | command "別のなにか(関数名)" (または command "$data" "別のなにか") 例えばだけどこんな感じにして、commandで$dataを1行ずつ処理して その都度、別のなにか(関数)を呼び出す。そうするとサブシェルにはならないので ストリーミングで処理しつつ、グローバル変数経由で呼び出し元に結果を返すことができる この時、別の何かがシェル関数の場合に、commandが外部コマンドだとシェル関数を呼び出せないので 連鎖的にcommandもシェル関数として実装。みたいなこともやってたりする 10. 移植性のあるシェルプログラミング http://web.sfc.wide.ad.jp/ ~sagawa/gnujdoc/autoconf-2.59/autoconf-ja_10.html >>196 なるほど。 俺は https://www.ipsj.or.jp/dp/contents/publication/32/S0804-R1601.html ここの3.1.1を読んで、制御文をなるべく使わない方向に開発をシフトしてみたんだよね。 まあどうしても必要な部分はあなたも云ってる通りもちろんあるけど。 >>198 別にその記事に大きく反対したいわけじゃないけど おかしな点は指摘しておかないといけない その手続き型コーディングがステップ数が多く処理効率が低いと書いてある所だが その原因はストリーミング型コーディングのせいじゃない rmコマンドの発行回数が原因だ ■手続き型コーディング(ステップ数が多く処理効率が低い) i=3 while [ $i -le 10000 ]; do file="file${i}.txt" rm -f "$file" # 何度もrmコマンドを実行している i=$((i+3)) done 結果 real 0m3.232s user 0m2.488s sys 0m0.808s ■ストリーミング型コーディング awk 'BEGIN{for(i=3;i<=10000;i+=3){print i;}}' | sed 's/.*/file&.txt/' | xargs rm -f 結果 real 0m0.040s user 0m0.004s sys 0m0.040s プロセスが分かれており並列で動く可能性があるから 多少ストリーミング型の方が早くはなるとは思うが 以下のように修正すると差は殆ど無くなる。 ■手続き型コーディング修正版 i=3 files() { while [ $i -le 10000 ]; do file="file${i}.txt" echo $file i=$((i+3)) done } rm -f $(files) 結果 real 0m0.052s user 0m0.018s sys 0m0.044s 余談だがこれはLinuxで実行した結果だがforkが遅いWSLだと 起動プロセスが多い分逆転するかもしれない 訂正 その手続き型コーディングがステップ数が多く処理効率が低いと書いてある所だが その原因は手続き型コーディングのせいじゃない >>200 非本質的だけど rm -f $(files)とかだとARG_MAXに引っ掛かる可能性がない? 尤も手元の機械では $ getconf ARG_MAX 2097152 だったので無問題かも知れないけど。 >>202 > rm -f $(files)とかだとARG_MAXに引っ掛かる可能性がない? あるよ。それならxargsを使えばいいし、 xargsはファイルから読み込む方法もあるからパイプ使わなくても使える。 ともかく速さの理由はストリーミング型コーディングではないと言いたかっただけ だいたいawkの中に書いてあるコードは手続き型だろといいたいし、 ステップ数だってちょっと書き方変えれば、大差ないってのがわかるだろう i=3; while [ $i -le 10000 ]; do rm -f "file${i}.txt" i=$((i+3)); done awk 'BEGIN{for(i=3;i<=10000;i+=3){print i;}}' | sed 's/.*/file&.txt/' | xargs rm -f お題 関数fooに対して3つ以上の引数を渡した時、1番目と2番目の引数を入れ替え以下の例のように出力せよ (引数が3つ未満の場合は考慮する必要はなし) ただし、シェルビルトイン関数のみを使い、変数の使用は禁止とする (ここでいう変数とはsetコマンドで表示されるもののことである) foo 1 2 3 4 5 2 1 3 4 5 レベル1: 引数に使用する文字は英数のみとする レベル2: 引数にダブルクォート、シングルクォート、スペース、タブが含まれていても動作するようにせよ あ、いかん。>>204 は簡単な解答があるわw ちょっと修正する お題(>>204 の訂正版) 次のような関数barがある。 bar() { echo 'begin'; printf '%s\n' "$@"; echo 'end'; } 3つ以上の引数を渡した時、1番目と2番目の引数を入れ替えて関数barを呼び出す関数fooを作成し 以下の例のような出力をせよ(引数が3つ未満の場合は考慮する必要はなし) ただし、シェルビルトイン関数のみを使い、変数の使用は禁止とする (ここでいう変数とはsetコマンドで表示されるもののことである) foo 1 2 3 4 5 [出力] begin 2 1 3 4 5 end レベル1: 引数に使用する文字は英数のみとする レベル2: 引数にダブルクォート、シングルクォート、スペース、タブが含まれていても動作するようにせよ 余談だが、 func() { printf '%s\n' "begin$@end";} という関数の時、 func 1 2 3 4 5 って書いたら begin1 2 3 4 5end func 3 って書いたら begin3end って表示されるんだよな。 理解はできるが、なんか不思議だ func -a --foo file1 -b file2 -c --bar みたいな、オプションとファイル名が交互に来てるときに オプションを全部解析し終わってから ファイルに対して処理を実行していくのってけっこう大変なんだな forで回すのもwhileで回すのも一筋縄ではいかない もちろんbashなんかの配列を使わないという前提 func -a --foo foo1 file1 -b file2 -c --bar あ、例えば、foo1は--fooはパラメータ、のようなものが有る場合だった -で始まる or 始まらないだけで見分けるならまだ簡単 そもそもそんな指定の仕方ありうる? ありえない状況を想定してヤキモキするのは非生産的だと思うんだけど……。 >>211 よくやるよ。 前打ったコマンドをヒストリで呼び出して、後ろにオプション追加して実行とか >>212 仮にそうだとして ということは >>210 を正規化≠キると func -a --foo foo1 -b -c --bar -- file1 file2 っていうことかな? GNUのgetoptのロングのやつ使えればなんとかなるんじゃないか? これでいいな。 $ getopt --options 'a,b,c' --longoptions 'foo:,bar' -- -a --foo foo1 file1 -b file2 -c --bar 195から207まで丸ごと吹っ飛んでんだがどんだけレスバトルしてたんだ 見る気もないが >>218 たのむから一つ前のレスくらい読んでくれ>>215 >>219 だからMacのgetoptはGNUのgetpotではないから動かないって言ったんだけど? GNUのgetoptなら動く。そうでないなら動かないと言われても、 どの環境がGNUのgetoptかなんてわからないでしょう? GNUのgetoptがほとんどの環境で使われているなら問題ないが 使われてな環境は多いかもしれない。そしてそれがMacという形で 現実になったということを書いたんだけど >>220 ああそうなの。 Macのgetopt(1)がGNU製じゃないことを知らないような書きぶりだったから てっきり。 すまんな つーか孰れにしても可搬性は皆無w やはりgetoptは可搬性は皆無か 日本語でgetoptでぐぐるとこれが上の方に出てくるけど 相変わらずの、qiitaクオリティってことでいいのかな? https://qiita.com/b4b4r07/items/dcd6be0bb9c9185475bb > 一般に 使用される getopt のテンプレートは以下です。 > > getopt.sh > set -- 'getopt ad: "$@"' 初っ端からおかしいし。バッククォート書いたつもりが、シングルクォートに化けた? しかもset --じゃだめだろうと思ってたら、後半では正しい書き方してたが > OPT=`getopt -o ab:c --long long-a, long-b:,long-c -- "$@"` > 略 > eval set -- "$OPT" で、次 > getopt には重大な落とし穴があります。それは、スペースや特殊文字が > 引数に含まれていた場合、正しく処理できないということです。 特殊文字まではやってないが、スペース入れても普通に動いたんだが? デフォルトでクォートされてるよな? man getoptすると「伝統的なgetoptの実装では 〜略〜 空白 〜略〜 使うことができない 〜略〜 この実装ではクォートした出力を生成する」ってことは(この記事で言及できないほど)最近対応したのか? それはそれとしてクォート方法の指定ができて、shとbash(違いあるの?)があるからシェルごとに 使い分けなきゃいけない気がしていやなんだが ま、この記事のBSD系ではロングオプション非対応っていうのはgetoptを候補からすばやく外すのには役に立ったなw GNU版ならもう少し色々できることがわかったが、やはり大変だな。今回は書かなかったがサブコマンド対応とか。 あとgetopt使っても自前処理でもコードの量に大差ないってのはなんなだろう?と前から思っている parsargコマンドみたいなのを再発明したほうが早いかもね GNU getoptと同じ(もしくはもうちょっと拡張)挙動でかつ可搬なのを。 質問しようと思ったけど初心者の俺にはレベル高い話ばかりで恥ずかしいぜ、、 ありがとう やりたいことは、環境を判定して対象の環境にある複数サーバーに対して一括でコマンド発行したい、です 条件 ・A環境とB環境があり、引数でそれぞれ指定し(test.sh A のようにしたい)、シェルスクリプト側でどちらが入力されたか判定する ・サーバーにssh接続するためのIPアドレス等は外部ファイルに記載されているため、環境判定後にファイルを読み込んでsshアクセスする必要がある ・サーバーは両環境それぞれ25台ずつあるが、5台ずつコマンドを発行し、waitしてからまた5台と、塊毎にやりたい 質問1 外部ファイルの指定方法が分からないです ifで環境を判定してthenの後にファイルを指定すれば良いのか? 質問2 5台ずつ、を実現するためにはwhileで良いのか? そもそも、sshをまとめてやって後からそれらにコマンド発行ってできるのか 初歩的すぎて恥ずかしい限りですがお願いします >>227 自己解決 やり方を変えることにしました ssh ... [command] ができるのがわかったとかじゃないのかな >>232 233の言うとおりです opensshのマニュアル呼んだら、ファイル指定もコマンド発行方法も書いてあったので、それでやることに あとはifとの組み合わせで頑張ります まぁつまずいてるんだけど 私たち日本人の、日本国憲法を改正しましょう。 総ム省の、『憲法改正國民投票法』、でググって みてください。拡散も含め、お願い致します。 ksh、関数のオーバーライド、くせっていうかバグあるわ (foo() { echo 1; }; foo; (foo() { echo 2; }; foo;); foo) 何度実行しても1 2 1 と表示される(想定通り) foo() { echo 1; }; foo; (foo() { echo 2; }; foo;); foo 最初の1回は1 2 1 と表示されるが、 2回目以降は1 1 1 としか表示されない(2でオーバーライドできなくなる) もう一つ。kshで関数の定義の場所でオーバーライドできない 場合があるんだが原因がよくわからない。 多分これ関連なんだろうけど https://github.com/modernish/modernish/blob/master/libexec/modernish/cap/BUG_FNSUBSH.t これはメインシェルの定義がある場合、サブシェルの中での定義が無視されるということだから、 サブシェルの中で定義していれば、その中のサブシェルではオーバーライドできると解釈できる たしかに>>236 はそのとおりの動きはしてる もう一つの問題も同じなんだろうか、定義の順番を変えればうまくいくんだが サブシェルに関しては定義の順番を変えても同じだと思うんだけどなぁ。 .コマンドを使ってるのが関係してるのかなんだこりゃって感じだ もう一つの問題は、ちょっとコードが入り組んでて調べるのが面倒くさい。 なんでこれで解決したのかわかってない状況だが、今のところ動いているので まあなにかわかったら書くよw 初心者です、while文の質問です このようなスクリプトがあったとします a=0 while [ $a -le 50 ] do a=`expr $a +1` echo "${a}個目 done とやると、50個目まで表示されると思いますが、この数値が5の倍数の時だけ、echoで表示させたあとsleepを入れるようにするには、contiuneを使えばよい? それともifでやる? よくわからず >>239 continue はループ内のその後の処理を飛ばすやつだからできないと思うが。 普通に if 使ってやるしかないのでは? if [ `expr $a % 5` -eq 0 ] ; then sleep 1 ; fi とか、done の直前に入れる。 >>240 > continue はループ内のその後の処理を飛ばすやつだからできないと思うが。 5の倍数以外でsleepを飛ばせばできるYO! >>239 > とやると、50個目まで表示されると思いますが 51個目まで表示される > a=`expr $a +1` は a=$((a+1)) の方が良い シェルによって実行されるから速い 俺だったらif使わないでこう書くかな [ $(($a % 5)) = 0 ] && sleep 1 ただこの書き方は if を使った場合と完全に等価なわけじゃなくて 後続行実行時点でのexit codeが異なることがあるから使い方に注意だけど ついでだから書いておくと、 a=`expr $a +1` でも大差ないだろ?と思った人 WSLの酷さがわかるぞ たった50回のループ、sleepを行わない状態で a=$((a+1)) だと 約0.025秒なのに、 a=`expr $a +1` だと 約0.5秒にもなるのだ 10000回のループだと a=$((a+1)) だと 約0.1秒のところ、 a=`expr $a +1` だと 約90秒 早くWSLのfokの遅さが解決すると良いな × [ $(($a % 5)) = 0 ] && sleep 1 ○ [ $((a % 5)) = 0 ] && sleep 1 書き込む前に気づいて直したと思ったんだがコピペミスったか 上でも動かないわけではない。無駄なだけ > [ $((a % 5)) = 0 ] 新旧混ぜんなw arithmetic使うなら、そのままevaluation記法にしてしまえばいい。 ((...)) あと、旧testでの数値比較は-eqを使え。 >>245 ん? どういうふうに書くってこと? >>237 の件、ぼや〜っとだけどわかりかけてきた気がする スクリプトのコードが.(ドット)コマンドで評価された時点で、 関数がすでに存在している場合、その関数を使用するコードがあれば 事前にリンクされているような動きをしている その場合サブシェルで関数を再定義しようとしてもできないんじゃないかな だから事前にリンクできないようにeval呼び出しに変更したら 再定義された方の関数を呼び出すようになった ((a % 5 == 0))&& ... なんで一々ここまで言わんとわからんような奴が、他人の指摘しているんだろうかね。。。 -eqでの数値比較とか基本中の基本だろうに。 >>247 $ dash -c 'a=0; ((a % 5 == 0)) && echo ok' dash: 1: a: not found $ dash -c 'a=0; (($a % 5 == 0)) && echo ok' dash: 1: 0: Permission denied なぜかって? その書き方はPOSIX準拠じゃないもの https://github.com/koalaman/shellcheck/wiki/SC2039#standalone- 動かないコード出されて、偉そうにされても やれやれって言うしか無いわなw >>239 だけど、奥が深いね if使わずにもできたりするのか はぁ、先は長そうだ、ありがとう cat file.txt | ( head; tail) は最高 >>224 > parsargコマンドみたいなのを再発明したほうが早いかもね 違うもの調べてて、こういうのを見つけた。 見つけたってだけでどんなものかは調べていない http://zentoo.hatenablog.com/entry/20110708/1310124831 > ちょっとした理由でgit-flow (https://github.com/nvie/gitflow ) のソースを > ちょっとちょっと眺めていたんだけど、内部でコマンドライン引数を > パースするためにshflagsというツールが使われていたのでメモ。 猫好きてそんなに言語化しにくい複雑怪奇な感情持っとるんか? 例えば女をみて欲情しない人間に、なぜ欲情するのかを説明するのは難しいだろう 言葉を並べるだけなら本能だとか体の特徴なんかを言えるが、共感はされまい つまりそれを疑問に感じる人間には、その時点ですでに納得させる言葉がないのだ でもそもそも説明するのが困難なのやろ? おまえが言ったんやで なぜ説明するのが困難かを説明したつもりだったんだが つまんねーよ。シェルスクリプトに関係ある話をしろ ってか、>>206 お願い >>265 自分でそういう設定してんだろ。 ngしてますアピールうざいよ。 なんか遠いレスの番号という意味じゃないの。てか、なんで噛み付いているんだか ワッチョイ変わったのになんでNG効いてんだ?と思ったら課題マンだったのか あるパスがディレクトリで書き込み可能かは、[ -d "$path" ] とか [ -w "$path" ] で調べられるし、 所有者が自分自身であるかは-Gや-Oで調べられますが、 自分以外が読み書き不可能であることをチェックするにはどうしたら良いですかね? やっぱlsしかないですかね。 このフォーマットって仕様化されてるんですよね >>271 pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html#tag_20_73_10 ありがとう。だがオプション多すぎて読むのだりぃw なんでだろ?どうもlsって解析する気にならないんだよな 人間向けのフォーマットでコンピュータが解釈するのに向いてない感じがしている どこまでPOSIXで仕様化されてるのか、どこからが拡張なのか、オプション多すぎて(略 関係ないけど、ふと本棚を見たら 「lsを読まずにプログラマを名乗るな!」という本があったw そういや買ったっけ(未読) >>273 >>272 はPOSIXの仕様。>>269 ,270,271の目的には If the -l option is specified, the following information shall be written for files other than character special and block special files: と If the -l option is specified, the following information shall be written for character special and block special files: で書いてあるフォーマットだけだけどね そういや、macだとlsで drwxr-xr-x+ や drwxr-xr-x@ みたいに 後ろになにかくっついていることがあるな > Implementations may add other characters to this list to represent other implementation-defined file types. くっつけても良いってことなのか? stat コマンドの出力解析した方が楽ではないか? または find コマンドの -perm 利用してチェックして -exec でやらせたいことをやらせる。 >>279 そこででてくるのが、またPOSIXですよ statはPOSIXで規定されてない SolarisやUP-UXには存在しない 更にLinuxとMacでオプションもフォーマットもぜんぜん違う 標準入力の代わりにfileの内容を入力するのに、リダイレクトで $ <file somecmd とやる方式ってPOSIXで既定されてるっけ。 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_01 このへんに書いてそうなんだけど、それっぽい記述がない? >>280 じゃ、find で。 ファイルのパーミッションに 066 を and した時に 0 になるやつでいいのなら find $dir -prune ! -perm -40 ! -perm -20 ! -perm -4 ! -perm -2 -exec やらせたいこと \; のようにすればいいのではないかな。 http://pubs.opengroup.org/onlinepubs/9699919799/ で find コマンド見てみたら -perm はあるようなので。 もし GNU の find コマンド使えるようなら -perm /mode が使えて、これは何れかのビットが立っていれば 真になるので ! -perm /66 の一つで出来ると思う。 GNUのfind使っていいという条件なら、GNUのlsやstatでいいじゃんw その辺はご自由に。POSIX縛りがあってもこの場合にfindも使えるってだけの話なので。ls使いたいならそうすればいい。 >>281 2.1の5で、リダイレクトの処理をしてパラメーターリストから 除去するって書いてあるのと、 6でコマンド名を第0パラメーターとして渡すって書いてるってことは、 先頭もパラメーターリストの一部と解釈できるから、 暗に、先頭にリダイレクトがあってもいいってことを表してるんでは? >>286 明示はされてないのね。 でもありがとう。多分 大丈夫ということでしょう。 >>118 それはオブジェクト指向の話じゃない C++の話だ そもそもテンプレートやジェネリックは、型を明確にすることでバグを少なくしたり 速度を速くするためのもので、オブジェクト指向における問題点を解決するための追加機能 テンプレートやジェネリックを使うことでオブジェクト指向が不要になるのではなく オブジェクト指向と組み合わせて使うことで問題点が改善される テンプレートはオブジェクト指向と組み合わせずに使えたと思うが、 テンプレートの殆ど(ジェネリックは全て)はオブジェクト指向なしに使うことはできない あ、書き込まれてないと思ったら書き込む場所間違えてたのかw シェルというか tar コマンドの問題なんだけど、tar tvf でアーカイブに入っているファイルのタイムスタンプを秒単位まで出す方法ある? Linux で GNU tar のmanページ見た感じではなさそうなんだけど、やはり自作するしかないなかな。 >>292 tar --full-time ... ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる