X



シェルスクリプト総合 その29

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさん (ワッチョイ 3efb-m2E0)
垢版 |
2018/09/22(土) 11:53:21.38ID:BBiLRgnj0
!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
0196デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/05(金) 08:48:03.27ID:bSHFQ9XI0
>>195
ストリーミング指向と制御文はあまり関係ないと思うよ
流れてくるものに対してなにか処理をしたいなら必ず
制御文を使うことになるし

ストリーミング指向の話をするなら、関数呼び出しし結果の標準出力を
変数にキャプチャするっていうのはストリーミング指向と反してると思う
例えば result=$(command "$data") みたいな形のことね
俺もこの書き方はなるべく避けるようになった

ストリーミング指向的には echo "$data" | command | 別のなにか
とするほうが良いだろう

ただ気づいたのがこの形だとサブシェルになってしまうから、
commandや別のなにかから、呼び出しの起点にデータを戻すことが
簡単にはできないという所。値を戻すこと自体がストリーミングの流れに反しているし
かと言ってそれが必要な場合もある。例えば処理した行数をカウントしたりとかね。

なので最近はイベントハンドラ方式を使うようになった
echo "$data" | command "別のなにか(関数名)" (または command "$data" "別のなにか")

例えばだけどこんな感じにして、commandで$dataを1行ずつ処理して
その都度、別のなにか(関数)を呼び出す。そうするとサブシェルにはならないので
ストリーミングで処理しつつ、グローバル変数経由で呼び出し元に結果を返すことができる

この時、別の何かがシェル関数の場合に、commandが外部コマンドだとシェル関数を呼び出せないので
連鎖的にcommandもシェル関数として実装。みたいなこともやってたりする
0199デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/06(土) 08:20:34.66ID:Sfai+wMy0
>>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
0200デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/06(土) 08:23:22.22ID:Sfai+wMy0
プロセスが分かれており並列で動く可能性があるから
多少ストリーミング型の方が早くはなるとは思うが
以下のように修正すると差は殆ど無くなる。

■手続き型コーディング修正版
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だと
起動プロセスが多い分逆転するかもしれない
0203デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/06(土) 11:49:41.89ID:Sfai+wMy0
>>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
0204デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/07(日) 07:00:55.51ID:9DLOHMhB0
お題

関数fooに対して3つ以上の引数を渡した時、1番目と2番目の引数を入れ替え以下の例のように出力せよ
(引数が3つ未満の場合は考慮する必要はなし)

ただし、シェルビルトイン関数のみを使い、変数の使用は禁止とする
(ここでいう変数とはsetコマンドで表示されるもののことである)

foo 1 2 3 4 5
2
1
3
4
5

レベル1: 引数に使用する文字は英数のみとする
レベル2: 引数にダブルクォート、シングルクォート、スペース、タブが含まれていても動作するようにせよ
0206デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/07(日) 07:13:43.37ID:9DLOHMhB0
お題(>>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: 引数にダブルクォート、シングルクォート、スペース、タブが含まれていても動作するようにせよ
0207デフォルトの名無しさん (ワッチョイ 1e98-kArq)
垢版 |
2018/10/07(日) 07:18:54.69ID:9DLOHMhB0
余談だが、

func() { printf '%s\n' "begin$@end";} という関数の時、

func 1 2 3 4 5 って書いたら

begin1
2
3
4
5end

func 3 って書いたら
begin3end

って表示されるんだよな。

理解はできるが、なんか不思議だ
0209デフォルトの名無しさん (ワッチョイ a798-kArq)
垢版 |
2018/10/08(月) 18:41:40.22ID:IMi/szTI0
func -a --foo file1 -b file2 -c --bar

みたいな、オプションとファイル名が交互に来てるときに
オプションを全部解析し終わってから
ファイルに対して処理を実行していくのってけっこう大変なんだな
forで回すのもwhileで回すのも一筋縄ではいかない
もちろんbashなんかの配列を使わないという前提
0215デフォルトの名無しさん (ワッチョイ 969f-XQ2D)
垢版 |
2018/10/08(月) 23:31:18.49ID:tKqgyITq0
GNUのgetoptのロングのやつ使えればなんとかなるんじゃないか?
0221デフォルトの名無しさん (ワッチョイ a798-kArq)
垢版 |
2018/10/09(火) 07:57:18.12ID:UgeI4/Dm0
GNUのgetoptなら動く。そうでないなら動かないと言われても、
どの環境がGNUのgetoptかなんてわからないでしょう?

GNUのgetoptがほとんどの環境で使われているなら問題ないが
使われてな環境は多いかもしれない。そしてそれがMacという形で
現実になったということを書いたんだけど
0223デフォルトの名無しさん (ワッチョイ a798-kArq)
垢版 |
2018/10/09(火) 08:18:38.20ID:UgeI4/Dm0
やはり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使っても自前処理でもコードの量に大差ないってのはなんなだろう?と前から思っている
0227デフォルトの名無しさん (スッップ Sdff-ehAa)
垢版 |
2018/10/15(月) 15:50:29.54ID:Q15q2Q7Cd
ありがとう
やりたいことは、環境を判定して対象の環境にある複数サーバーに対して一括でコマンド発行したい、です

条件
・A環境とB環境があり、引数でそれぞれ指定し(test.sh A のようにしたい)、シェルスクリプト側でどちらが入力されたか判定する

・サーバーにssh接続するためのIPアドレス等は外部ファイルに記載されているため、環境判定後にファイルを読み込んでsshアクセスする必要がある

・サーバーは両環境それぞれ25台ずつあるが、5台ずつコマンドを発行し、waitしてからまた5台と、塊毎にやりたい

質問1
外部ファイルの指定方法が分からないです
ifで環境を判定してthenの後にファイルを指定すれば良いのか?

質問2
5台ずつ、を実現するためにはwhileで良いのか?
そもそも、sshをまとめてやって後からそれらにコマンド発行ってできるのか

初歩的すぎて恥ずかしい限りですがお願いします
0228デフォルトの名無しさん (アウアウカー Sa7b-GU/K)
垢版 |
2018/10/15(月) 15:55:57.71ID:oz1FOCTra
どうぞどうぞ
0232デフォルトの名無しさん (アウアウカー Sa7b-GU/K)
垢版 |
2018/10/15(月) 18:56:32.85ID:3C5NcaRXa
>>229
どうやることにしたの?
0234デフォルトの名無しさん (ワッチョイ 27eb-ehAa)
垢版 |
2018/10/15(月) 21:27:00.70ID:0gVRdy5i0
>>232
233の言うとおりです
opensshのマニュアル呼んだら、ファイル指定もコマンド発行方法も書いてあったので、それでやることに
あとはifとの組み合わせで頑張ります
まぁつまずいてるんだけど
0236デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 12:14:09.96ID:/ofNkRJS0
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でオーバーライドできなくなる)
0237デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 12:32:51.57ID:/ofNkRJS0
もう一つ。kshで関数の定義の場所でオーバーライドできない
場合があるんだが原因がよくわからない。

多分これ関連なんだろうけど
https://github.com/modernish/modernish/blob/master/libexec/modernish/cap/BUG_FNSUBSH.t
これはメインシェルの定義がある場合、サブシェルの中での定義が無視されるということだから、
サブシェルの中で定義していれば、その中のサブシェルではオーバーライドできると解釈できる
たしかに>>236はそのとおりの動きはしてる

もう一つの問題も同じなんだろうか、定義の順番を変えればうまくいくんだが
サブシェルに関しては定義の順番を変えても同じだと思うんだけどなぁ。
.コマンドを使ってるのが関係してるのかなんだこりゃって感じだ
0238デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 12:40:28.86ID:/ofNkRJS0
もう一つの問題は、ちょっとコードが入り組んでて調べるのが面倒くさい。
なんでこれで解決したのかわかってない状況だが、今のところ動いているので
まあなにかわかったら書くよw
0239デフォルトの名無しさん (スッップ Sd8a-YVHm)
垢版 |
2018/10/18(木) 16:05:20.75ID:q7k/ngFed
初心者です、while文の質問です
このようなスクリプトがあったとします
a=0
while [ $a -le 50 ]
do
a=`expr $a +1`
echo "${a}個目
done

とやると、50個目まで表示されると思いますが、この数値が5の倍数の時だけ、echoで表示させたあとsleepを入れるようにするには、contiuneを使えばよい?
それともifでやる?
よくわからず
0240デフォルトの名無しさん (アウアウカー Safb-nHS7)
垢版 |
2018/10/18(木) 16:38:15.26ID:hfM3a7YWa
>>239
continue はループ内のその後の処理を飛ばすやつだからできないと思うが。

普通に if 使ってやるしかないのでは?
if [ `expr $a % 5` -eq 0 ] ; then sleep 1 ; fi
とか、done の直前に入れる。
0241デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 17:31:05.73ID:/ofNkRJS0
>>240
> continue はループ内のその後の処理を飛ばすやつだからできないと思うが。
5の倍数以外でsleepを飛ばせばできるYO!

>>239
> とやると、50個目まで表示されると思いますが
51個目まで表示される

> a=`expr $a +1`
は a=$((a+1)) の方が良い
シェルによって実行されるから速い

俺だったらif使わないでこう書くかな
[ $(($a % 5)) = 0 ] && sleep 1
ただこの書き方は if を使った場合と完全に等価なわけじゃなくて
後続行実行時点でのexit codeが異なることがあるから使い方に注意だけど
0242デフォルトの名無しさん (アウアウカー Safb-nHS7)
垢版 |
2018/10/18(木) 17:40:57.72ID:hfM3a7YWa
あ。そうか。なるほど。
0243デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 17:41:55.70ID:/ofNkRJS0
ついでだから書いておくと、
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の遅さが解決すると良いな
0245デフォルトの名無しさん (ワッチョイ 6a7c-fIkj)
垢版 |
2018/10/18(木) 17:56:39.61ID:uRBurQVb0
> [ $((a % 5)) = 0 ]
新旧混ぜんなw
arithmetic使うなら、そのままevaluation記法にしてしまえばいい。
((...))
あと、旧testでの数値比較は-eqを使え。
0246デフォルトの名無しさん (ワッチョイ 7b98-usPd)
垢版 |
2018/10/18(木) 18:24:07.81ID:/ofNkRJS0
>>245
ん? どういうふうに書くってこと?

>>237の件、ぼや〜っとだけどわかりかけてきた気がする

スクリプトのコードが.(ドット)コマンドで評価された時点で、
関数がすでに存在している場合、その関数を使用するコードがあれば
事前にリンクされているような動きをしている

その場合サブシェルで関数を再定義しようとしてもできないんじゃないかな
だから事前にリンクできないようにeval呼び出しに変更したら
再定義された方の関数を呼び出すようになった
0247デフォルトの名無しさん (ワッチョイ 6a7c-fIkj)
垢版 |
2018/10/18(木) 18:35:46.85ID:uRBurQVb0
((a % 5 == 0))&& ...
なんで一々ここまで言わんとわからんような奴が、他人の指摘しているんだろうかね。。。
-eqでの数値比較とか基本中の基本だろうに。
0250デフォルトの名無しさん (アウアウカー Safb-nHS7)
垢版 |
2018/10/18(木) 19:33:42.29ID:9i8IzX+1a
(( )) ではなく [[ ]] では?
0255デフォルトの名無しさん (ワッチョイ f398-usPd)
垢版 |
2018/10/20(土) 02:22:05.90ID:V0y1IBp60
>>224
> parsargコマンドみたいなのを再発明したほうが早いかもね

違うもの調べてて、こういうのを見つけた。
見つけたってだけでどんなものかは調べていない

http://zentoo.hatenablog.com/entry/20110708/1310124831
> ちょっとした理由でgit-flow (https://github.com/nvie/gitflow) のソースを
> ちょっとちょっと眺めていたんだけど、内部でコマンドライン引数を
> パースするためにshflagsというツールが使われていたのでメモ。
0258デフォルトの名無しさん (オッペケ Srb3-iHQf)
垢版 |
2018/10/20(土) 12:37:11.93ID:MzLaXRRvr
猫好きてそんなに言語化しにくい複雑怪奇な感情持っとるんか?
0259デフォルトの名無しさん (ワッチョイ 3bfb-6CEe)
垢版 |
2018/10/20(土) 12:43:08.81ID:o8LOHz9N0
例えば女をみて欲情しない人間に、なぜ欲情するのかを説明するのは難しいだろう
言葉を並べるだけなら本能だとか体の特徴なんかを言えるが、共感はされまい
つまりそれを疑問に感じる人間には、その時点ですでに納得させる言葉がないのだ
0260デフォルトの名無しさん (オッペケ Srb3-iHQf)
垢版 |
2018/10/20(土) 12:48:37.62ID:MzLaXRRvr
でもそもそも説明するのが困難なのやろ?
おまえが言ったんやで
0269デフォルトの名無しさん (ワッチョイ ea98-usPd)
垢版 |
2018/10/21(日) 03:41:54.16ID:dQhyPQp/0
あるパスがディレクトリで書き込み可能かは、[ -d "$path" ] とか [ -w "$path" ] で調べられるし、
所有者が自分自身であるかは-Gや-Oで調べられますが、
自分以外が読み書き不可能であることをチェックするにはどうしたら良いですかね?
0273デフォルトの名無しさん (ワッチョイ ea98-usPd)
垢版 |
2018/10/21(日) 06:34:56.07ID:dQhyPQp/0
ありがとう。だがオプション多すぎて読むのだりぃw

なんでだろ?どうもlsって解析する気にならないんだよな
人間向けのフォーマットでコンピュータが解釈するのに向いてない感じがしている
どこまでPOSIXで仕様化されてるのか、どこからが拡張なのか、オプション多すぎて(略

関係ないけど、ふと本棚を見たら
「lsを読まずにプログラマを名乗るな!」という本があったw
そういや買ったっけ(未読)
0276デフォルトの名無しさん (ワッチョイ 2aa2-k6PT)
垢版 |
2018/10/21(日) 06:45:46.73ID:xQoMnSHc0
>>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:
で書いてあるフォーマットだけだけどね
0277デフォルトの名無しさん (ワッチョイ ea98-usPd)
垢版 |
2018/10/21(日) 07:33:05.64ID:dQhyPQp/0
そういや、macだとlsで

drwxr-xr-x+ や
drwxr-xr-x@ みたいに
後ろになにかくっついていることがあるな

> Implementations may add other characters to this list to represent other implementation-defined file types.
くっつけても良いってことなのか?
0279デフォルトの名無しさん (アウアウウー Sa9f-L0SM)
垢版 |
2018/10/21(日) 20:00:09.31ID:0dWokXAIa
stat コマンドの出力解析した方が楽ではないか?
または find コマンドの -perm 利用してチェックして -exec でやらせたいことをやらせる。
0282デフォルトの名無しさん (ワッチョイ 269f-nBLa)
垢版 |
2018/10/22(月) 02:54:50.03ID:HyPH6OEy0
>>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 の一つで出来ると思う。
0284名無しさん@そうだ選挙に行こう! Go to vote! (アウアウウー Sa9f-L0SM)
垢版 |
2018/10/22(月) 10:18:21.67ID:8WYgXfgna
その辺はご自由に。POSIX縛りがあってもこの場合にfindも使えるってだけの話なので。ls使いたいならそうすればいい。
0286デフォルトの名無しさん (JP 0H1b-v9b0)
垢版 |
2018/10/22(月) 16:55:54.61ID:9s+yFzz3H
>>281
2.1の5で、リダイレクトの処理をしてパラメーターリストから
除去するって書いてあるのと、
6でコマンド名を第0パラメーターとして渡すって書いてるってことは、
先頭もパラメーターリストの一部と解釈できるから、
暗に、先頭にリダイレクトがあってもいいってことを表してるんでは?
0289デフォルトの名無しさん (ワッチョイ ea98-Pr2h)
垢版 |
2018/10/22(月) 21:35:03.82ID:oF7wf0Ce0
>>118
それはオブジェクト指向の話じゃない
C++の話だ

そもそもテンプレートやジェネリックは、型を明確にすることでバグを少なくしたり
速度を速くするためのもので、オブジェクト指向における問題点を解決するための追加機能

テンプレートやジェネリックを使うことでオブジェクト指向が不要になるのではなく
オブジェクト指向と組み合わせて使うことで問題点が改善される

テンプレートはオブジェクト指向と組み合わせずに使えたと思うが、
テンプレートの殆ど(ジェネリックは全て)はオブジェクト指向なしに使うことはできない
0292デフォルトの名無しさん (アウアウウー Sa9f-L0SM)
垢版 |
2018/10/23(火) 13:27:19.16ID:QwHjyvjPa
シェルというか tar コマンドの問題なんだけど、tar tvf でアーカイブに入っているファイルのタイムスタンプを秒単位まで出す方法ある?
Linux で GNU tar のmanページ見た感じではなさそうなんだけど、やはり自作するしかないなかな。
■ このスレッドは過去ログ倉庫に格納されています

ニューススポーツなんでも実況