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
0565デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 08:36:27.28ID:UVRb8J0Z0
>>562
個人的な特殊な状況下で気をつけてることならたくさんあるんだけどな
パイプを(なるべく)使わないとかwww

そうさなぁ、例えば意図せぬエラーで落ちるように、set -eを使う場合は
挙動をよく理解して使うこととかかな

set -eは戻り値がエラーになった時点で中断される

set -e
foo() { echo foo begin; bar; echo foo end; }
bar() { echo bar begin; baz; echo bar end; }
baz() { echo baz; false; }
foo

これを実行すると、foo -> bar -> baz の呼び出しの流れが、bazのfalseで中断されて
foo begin
bar begin
baz
と表示されるんだが

foo の代わりに if foo; then :; fi と実行すると
なんと、foo end も bar end も表示されるんだよ

つまり foo を if や && や || と組み合わせて使うと、エラー中断機能が無効化される
だから、比較関数みたいに if foo; then という使い方を想定している関数は
中でしっかりエラーチェックをしておくこと

他の言語の例外みたいに考えてはいけない
0566デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 08:39:06.36ID:UVRb8J0Z0
↑ これが理由で、set -eは使うな派もいるらしいねw

>>562
> 例えば「局所変数や局所関数が宣言できないのでなるべく使わない」とかさ。

俺は訳あってそうしてるけど、特定のシェルに限って良いのなら
localもしくはtypesetで局所変数使えるよ

ただし、他の言語と違ってレキシカルスコープじゃなくて
ダイナミックスコープなので注意が必要
これも気をつけていることかな
0568デフォルトの名無しさん (アウアウウー Sa05-ahuQ)
垢版 |
2018/11/09(金) 09:08:37.30ID:JsSTi+Gxa
>>563
パイプが詰まってんだから停止させるしか方法ないと思うが?
バッファ大きくしても出力側が速いなら何れ突っ掛かる事になるよね。
0571デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 09:24:33.15ID:UVRb8J0Z0
ん? あぁ、質問はコレね

> メモリにバッファリングするだろうから、それが溢れないような処置として
> そういう仕様は理解できるけど、どこかに書いてあるのかな?
>
> シェルの仕様っぽい気がするけど、OSも絡んでいそうな気もする
0572デフォルトの名無しさん (アウアウウー Sa05-ahuQ)
垢版 |
2018/11/09(金) 09:31:46.72ID:JsSTi+Gxa
どう考えてもOSの仕様だろう。
0573デフォルトの名無しさん (ワッチョイ 9164-TRt+)
垢版 |
2018/11/09(金) 09:40:49.31ID:3ZHBVzZ50
>>565
ありがとう。set -eは鬼門だね。前に棘かなにかでも纏められてた。

ところで パイプ使わないのはどうしてだ?
シェルスクリプトは入出力指向なんだから積極的にパイプを活用すべきでは。
0574デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 09:55:47.54ID:UVRb8J0Z0
>>573
パイプ使わないのはシェルスクリプト内部で完結する場合の話ね
外部コマンドとのインターフェースでは使う

(内部で)使わない理由は変数の共有がやりづらいから
ま、これは特殊な事例なのでw
0576デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 10:31:29.63ID:UVRb8J0Z0
> ああ。たしかにパイプ越しに変数を参照させようと思ったらexportして環境変数にしなくちゃらんもんな。

もうちょっと説明すると

foo() {
 output | bar | baz
}

fooで見えてる変数を、bar, bazに見せるのは簡単なんだよ。
exportしなくても見せられる。

outputが出力する1行をbarが解析して変数に入れたとして、その変数を
bazで見ることはできない(barでexportしても無理)
bazが複数行の入力を集計した結果の変数をfooで見ることが出来ない。とかね

1データを1行に詰め込めればパイプ経由でのデータ受け渡しは可能だが
それができないデータだと難しい

他の言語でやれって? それをシェルスクリプトでやらなきゃいけないというのが個人的な特殊な状況下というわけ
0577デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/09(金) 17:10:16.74ID:3ZHBVzZ50
シェルスクリプトでそのコマンドが実行された行数を取得する方法ってあるかな。
#!/usr/bin/env bashにすれば${LINENO}変数にその位置が格納されてるので簡単なんだけど
やっぱりもうすこし汎用的な方法で実現したい。
0578デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 19:41:43.60ID:UVRb8J0Z0
>>577
LINENOしかないよ。ただしご存知の通りLINENOは使えないシェルがある。
dashなんか機能的には搭載されてるがdebianなどでは無効にされてるというw

更にシェルによっては関数呼び出しを行った時にソースコードの行数ではなく
関数の頭からの行数になっていて、それも0から始まるものと1から始まるものがある

まともに使えるのはbashだけかな
0579デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/09(金) 20:27:42.80ID:3ZHBVzZ50
>>578
だよね〜。ファイルの中での自分の位置を調べるのはシェルスクリプトでは無理っぽいね。
Cのassert()関数みたいなのを自作したかったんだけど 機能が落ちるのは嫌だな(もういいけど)

ああ ちなみに
> 関数呼び出しを行った時にソースコードの行数ではなく
> 関数の頭からの行数になっていて
これGNU Bashのことを言ってるのなら関数じゃなくて別名にすればO.K.だよ
0580デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/09(金) 21:02:47.30ID:UVRb8J0Z0
>>579
シェルスクリプトの世界にJavaScriptのトランスパイラ技術が導入されて
ソースコードの一行一行にLINENOを設定するコードを埋め込めば
解決できると思うんだが、流石にそれをやろうとする人はいないかなw

> これGNU Bashのことを言ってるのなら関数じゃなくて別名にすればO.K.だよ
alias?うーん、なんか別の問題がでそう。
でもbashじゃなかったと思う。

前に調べたときのコードを見てみたが、
関数の頭からの行数になるのはzsh, dash, busybox ashで
zshは0から始まる。他は1から始まる。bashとkshはソースコードの行数で取れる。
バージョンは詳しく調べてないから多分古いやつは使えなかったりすると思う

なので他のシェルでエラーが起きたら、bashで実行してエラーの行番号を調べたりしているw
0581デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/10(土) 09:07:56.28ID:lBOHSSIo0
たとえばシェルアーカイブってPOSIXシェルでも可能だよね。
ああいう感じで自分が含まれるファイルを自分で走査できるんなら
行位置も取得できないかなぁと思ったんだよね……。

しつこいけどシェルスクリプト版のassert()関数が欲しい っていうか作りたいんだよねぇ
0582デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 09:34:29.76ID:8OkJCHKT0
>>581
assertだけなら簡単かもしれないな。

assert関数の中で全てを処理するのは難しいと思う。自分のスクリプト名でさえ取得できない場合があるから
仮に取得できたとしても、何かしらのマーカーがないとどのassertかが区別できない。
(全ててのassertに区別できるマーカーを入れるっていうのなら可能だけどw)

前提としてLINENOが取得できないのはどうしようもないのでコード変換を行なう

foo() {
  asesrt [ i -gt 0 ]
}

みたいなコードがあったら以下みたいに、行番号を埋め込むプログラムを作る
(ファイル名もあったほうが良いかもしれない)

foo() {
  asesrt 2 [ i -gt 0 ]
}

これはPOSIXシェルスクリプトの範囲でもできる。一行づつ読んでパターンにマッチしたら変換するだけ
(複数行文字列やヒアドキュメントがあったら面倒だけど、対応してませんでもいいと思う)

あとは以上の変換処理を行って実行するラッパースクリプトを作って実行する
変換したコードはevalもしくはシェルにパイプで流し込むことで実行できる。
0583デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 09:39:54.31ID:8OkJCHKT0
あー、そうか、assertか、ならデバッグ時のみしか有効じゃなくていいな。

なら、普通にスクリプトを実行したときは、
何もしないassert関数になって、

debug.sh script.sh みたいにしたら、
コード変換を行ってから実行すれば良いのか

意外と自然な感じで作れそうw
0585デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/10(土) 16:43:37.38ID:lBOHSSIo0
色々考えてくれてマジでありがたい
確かにassert()関数はデバッグの時だけ有効になればいいから外部からスクリプトファイルを操作するという方法も
なんら不自然ではないな。
C言語のようにNDEBUG変数の有無によって処理を分けようとしてたけど
そっちのほうが柔軟な処理ができるのでいいね。

ていうかC言語と違ってシェルスクリプトのなかで安全・確実に変数を取り扱うのは厄介だから
寧ろNDEBUG変数は害悪ですらあるなw
0586デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 19:36:31.81ID:8OkJCHKT0
>>585
何もしないassert関数と書いたが、コメントの形でassertを入れるのはどうだろう?
これなら関数呼び出しすらないので、(わずかな)パフォーマンス低下も発生しないし
仮にスクリプト内でassert関数を使っていても問題ない
assert有効時は、長い名前に変換すれば良い
0587デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 19:38:01.66ID:8OkJCHKT0
あと、デバッグモード、assert関数有効時だけど、終了ステータスを変えてしまうことに注意な

foo() {
  false
  # assert [ なんちゃら ]
  echo $?
}

という場合、本来はechoで1と表示されるけど、単純にこのように変換してしまうと、
assert有効時に$?がassertの結果になってしまう
だから変換後はこんな感じかな? ||:を使うことでset -e状態でも落ちなくできる

foo() {
  false
  assertooooo 3 [ なんちゃら ] ||:
  echo $?
}

assertooooo() {
  EXIT_STATUS_BACKUP=$?
  LINE_NUMBER=$1
  shift
  if "$@"; then
    return $EXIT_STATUS_BACKUP
  else
    echo "エラー $LINE_NUMBER" >&2
    exit 1
  fi
}
0588デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 19:42:53.17ID:8OkJCHKT0
> 仮にスクリプト内でassert関数を使っていても問題ない
ちょっとわかりづらかったな

スクリプトでassertという名前の関数を別の用途で使っていた場合ってことね
0591デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 20:03:55.71ID:8OkJCHKT0
あー、バカだ

assertooooo 3 [ なんちゃら ] ||:
じゃなくて
assertooooo 3 [ なんちゃら ] &&:
だった

上はset -eでも落ちなくし、かつ終了ステータスを問答無用で0にする方法(エラーの時 : を実行する)
下がset -eでも落ちなくし、かつ終了ステータスはそのまま保つ方法
0592デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/10(土) 20:17:00.01ID:lBOHSSIo0
>>590
例えば
ファイルの名前がwhizprog.shだったとして
test "$( ps -p $$ -o 'comm=')" = 'whizprog.sh'
↑これが失敗・失敗しないで判定できるんじゃね?
尤もこれはシバン#!/bin/shというPOSIX未定義の構文を使ったばあいで
シバンを書いてないと使えない手だけど。
0593デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 20:22:22.65ID:8OkJCHKT0
>>592
ん? assert実行するためのソースコード書き換えの話だよ
debug.sh script.sh で読み込んだ、script.shの書き換えは簡単だけど、
その script.sh から . コマンドで読み込まれるコードまでは書き換えられない

だから . コマンドを検出して再帰的に・・・面倒なんだよなw

余談だが、
> test "$( ps -p $$ -o 'comm=')" = 'whizprog.sh'
これはPOSIX準拠なのだろうが、busyboxではpsはwしか使えないw
ps w
0594デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/10(土) 20:50:01.93ID:lBOHSSIo0
>>593

ああなるほど そんなことまでは考えてなかった。
正直,あなたに教えてもらった方法で大満足です。ありがとう。(欲を言えば行番号を自分で……しつこいねw)
とりあえずこんな感じに落ち着きました↓
#!/bin/sh

assert()
{
exec >&2
eval "test ${2}" || {
printf '%s: %d: %s\n' "${0}" ${1} "${3-"Assertion ${2} failed."}"
exit 1
}
}

main()
{
set -eu
umask 0022
export PATH="$( command -p getconf PATH ):${PATH}"

assert 18 '"a" = "b"' && :

exit $?
}

main "$@"
0595デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 21:06:21.14ID:8OkJCHKT0
update.sh script.sh でassert行の行番号を更新して上書き保存するってのもありかもw
それなら.コマンドの先も対応できる。いちいち更新しないといけないのが面倒だけど
0596デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/10(土) 21:44:08.29ID:lBOHSSIo0
更新するコマンドを別に作るんじゃなくてデバッグ時に更新も行うようにすれば(結局 手間は同じだけど)
だってデバッグする時ってその直前に必ず行番号やらを更新したいでしょう。
0598デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/10(土) 22:33:00.25ID:8OkJCHKT0
>>596
まあ、そうなるよねw
まあ、方針次第だな

>>597
ようするに、シェルスクリプトの世界にもトランスパイラが欲しいって話さ

ちなみにどれだったか忘れたがシェルスクリプトの文法を
解析するやつ(ASTを作る)はすでにある
それを使えばそんなに難しくはないと思う(とは言えやったことはない)
0600デフォルトの名無しさん (ワッチョイ b903-wpdm)
垢版 |
2018/11/10(土) 23:18:28.98ID:7gTt1pZ+0
もうシェルでなにしたいんやおまえ
0605デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/11(日) 00:30:20.21ID:PRctJ18Z0
>>603
ほ ん と そ れ

そして今調べて気が付いたがsh -xじゃねーなw
set -xだわ。

> The -a, -b, -C, -e, -f, -m, -n, -o option, -u, -v, and -x options
> are described as part of the set utility in Special Built-In Utilities.
> The option letters derived from the set special built-in
> shall also be accepted with a leading <plus-sign> ( '+' ) instead of a leading <hyphen-minus>
> (meaning the reverse case of the option as described in this volume of POSIX.1-2017).
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html#tag_20_117_04
0606デフォルトの名無しさん (ワッチョイ b903-wpdm)
垢版 |
2018/11/11(日) 00:55:22.01ID:TQKm/8h90
>>604
なんでワイに聞くねんwバカなんかおまえ?
0608デフォルトの名無しさん (ワッチョイ b903-wpdm)
垢版 |
2018/11/11(日) 01:00:58.12ID:TQKm/8h90
日本語が大丈夫ってどおゆう意味や?
0610デフォルトの名無しさん (ワッチョイ b903-wpdm)
垢版 |
2018/11/11(日) 01:09:59.72ID:TQKm/8h90
慌れるて日本語あるんか?ちょっと落ち着けやw
0612デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/11(日) 12:28:16.92ID:PRctJ18Z0
C言語ではなくシェルスクリプトでシグナルって活用してる?
kill(1)ユーティリティの-s KILLオプションくらいしか使ったことがないので
いまいち便利さが分からない。
利用者定義シグナルSIGUSER1とか 使いこなせたらシェルスクリプトでできることの幅が広がりそう
0613デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/11(日) 12:33:00.83ID:Tyd11AGx0
CTRL-Cされたときとかプログラム終了時に
作業ファイルを削除することぐらいかなぁ

あとはシグナルじゃないけど、bashの疑似シグナル
ERRとかDEBUGとかで遊んだことある

サービスなんかだとシグナル送ったら設定ファイル再読込したり
ddだったら進捗状況表示したりするね
0614デフォルトの名無しさん (ワッチョイ db9f-ki2E)
垢版 |
2018/11/11(日) 22:34:54.73ID:0F8Q4Ddx0
>>612
終了処理をちゃんとやらないといけないような処理の場合に trap 使ってシグナルに対して
特定の変数を 1 にするだけとか、そういうやり方で終了処理後にその変数見て 1 になって
いたら exit みたいな感じでシグナル利用するな。

後は普通に kill で他のプロセス終了させたい場合。pkill 使う事もある。
0615デフォルトの名無しさん (ワッチョイ 9164-TRt+)
垢版 |
2018/11/12(月) 12:58:25.45ID:JkRQG90v0
実行してるシェルスクリプトに行番号を付けるのさ,
PS4変数にn=$((n+1))みたいなのを入れてset -xとやればうまくいくかな と思ったんだが無理っぽいな。
どっかで無限ループして何も出力されないw
0620デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/14(水) 16:18:09.15ID:P8pvRdrO0
>>619
まあ俺も使い捨てのスクリプトでは使うけど
ただPOSIX 2017では
x=3; x=$((x+1))
↑これすら正常に動くとは定義されてないんだよね
ただ単に
x=3のとき$((x))は$(($x))と「同じ値になる」とだけ。
だから例示されるスクリプトでも
x=$(($x + 1))
みたいに書かれてる。
0624デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/14(水) 17:18:05.78ID:Da4Ohzbn0
あ、なるほど。わかった。

${a} って書いたときの、aの部分が式なんだ。$は含まれない。それと同じで
$((a)) もaの部分が式で>>623に書いてあるように、先に展開される仕様だから
$(($a))だと先に$aの部分が文字列として展開されて$((123))のように解釈される

だから https://github.com/koalaman/shellcheck/wiki/SC2004 に書いてあるように

> $ a='1+1'
> $ echo $(($a * 5)) # becomes 1+1*5
> 6

これだと、式が評価される前に文字列として展開されちゃうから変な計算になるが

> $ echo $((a * 5)) # evaluates as (1+1)*5
> 10

${a}と同じように$((a))と書くと、ちゃんと算術演算が行われるんだ

ということで、やっぱり$(($a))は間違いで$((a))が正しいようだね
0625デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/14(水) 18:02:30.17ID:Da4Ohzbn0
間違いっていうのは仕様違反って意味じゃなくて
算術演算をするなら$をつけないのが正しいって意味ね。

算術演算の前に$とかコマンドが展開されるっていうのも
仕様なのでそれも書いたとおりに動作する
0626デフォルトの名無しさん (オッペケ Srcd-3Pmj)
垢版 |
2018/11/14(水) 19:06:25.03ID:vF6ts/2/r
大きなファイルがいくつも入ったディレクトリのmvして分かったけど
ファイルがいくつあっても全部のcpが終わったあとにrmされるんだね
てっきり1ファイル毎かと思ってた
0627デフォルトの名無しさん (アウアウウー Sa05-pmE1)
垢版 |
2018/11/14(水) 19:23:07.48ID:bryEJhFFa
>>626
途中で中止した時に移動先を削除するだけで済むからじゃないかな。
0628デフォルトの名無しさん (ワッチョイ 9164-30bX)
垢版 |
2018/11/14(水) 19:34:11.25ID:P8pvRdrO0
>>625
んー。でも俺には
> If the shell variable x contains a value that forms a valid integer constant,
> optionally including a leading or ,
> then the arithmetic expansions "$((x))" and "$(($x))" shall return the same value.
は$((x))の「時だけ」$(($x))と等しいと読めるがな。
いずれにしても算術展開で算術計算をする際に変数を使いたいときは
$(( ($x) + 1 ))
にしてるわ。どっかで$(( x + 1 ))がエラーになるのが怖いので。
0630デフォルトの名無しさん (ブーイモ MMb3-D3se)
垢版 |
2018/11/14(水) 21:50:05.93ID:D6nmDs23M
$(())が展開できるものは限定されていて、$((x+1))が意図通りに展開される根拠は$((x))と$(($x))が等しいって書いてある一文しかない。
これは$(())の中だけはx=$xであると解釈できなくもないけど、算術演算をするなら$をつけないのが正しいとは読めないな。
0631デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/14(水) 22:59:44.88ID:Da4Ohzbn0
> $((x+1))が意図通りに展開される根拠は$((x))と$(($x))が等しいって書いてある一文しかない。
一つあれば十分じゃん。逆に展開されない根拠はないんだし

そもそも ${expression} と $((expression)) という記述があるんだから
expressionの中は同じ意味のはずだろう
0632デフォルトの名無しさん (ワッチョイ 9398-Nrm4)
垢版 |
2018/11/14(水) 23:06:12.49ID:Da4Ohzbn0
> どっかで$(( x + 1 ))がエラーになるのが怖いので。

それがエラーになるなら、とっくに検出してるだろうな。
複数のシェルで確認してるからさ

それ関連で注意が必要なのは $(($#-1))
zshだと81になる。
0636デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 11:23:09.13ID:gWaB1LhD0
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04_01
に書いてあるだろ

> Arithmetic expansion provides a mechanism for evaluating an arithmetic expression and substituting its value.
> The format for arithmetic expansion shall be as follows:
>
> $((expression))
>
> The expression shall be treated as if it were in double-quotes, except that a double-quote inside
> the expression is not treated specially. The shell shall expand all tokens in the expression for
> parameter expansion, command substitution, and quote removal.

まず、$(( )) の中にある。これがexpressionだ。
expand all tokens in the expression 式の中にある以下のトークンの展開、
for parameter expansion, command substitution, and quote removal.
パラメータ展開、コマンド置換、クォートの削除

> Next, the shell shall treat this as an arithmetic expression and substitute the value of the expression.
その次(Next)に この式を arithmetic expression (算術式)として扱う

つまりだ。パラメータ展開( $(($x))の$xの展開 )が行われるまでは、まだ算術式として扱う前なんだよ

そもそもだな。 x=123を見て分かる通り。変数名はxだぞ。$xじゃない。
他の言語では変数名が$で始まるものがあるが、シェルスクリプトにおいて、
$は、パラメータ展開、コマンド置換、算術展開等を行うための記号だって書いてあるだろ?

特殊変数の説明だって、$@じゃなくて@、$*じゃなくて*、$#じゃなくて#、$$じゃなくて$ と書いており
変数名とは最初の$は含まれない部分だってことが読み取れるだろ
0639デフォルトの名無しさん (ワッチョイ 9564-uG0O)
垢版 |
2018/11/15(木) 12:27:06.72ID:sJsqi8La0
>>638
発端は>>620
> > x=$(($x + 1))
>
> shellcheckではそれ不要だから外せって言われるよ?
> shellcheckが完璧だとは思わないけど、
> あえてそうしてるんだからPOSIXだと思うんだけどなぁ
これなんだが。
つまりx=$(($x + 1))の「$x」が不要という主張は完全に間違いってことだ。
x = $(($x - 1))とx = $((x - 1))のどっちが正しいかなんて言ってねえよ。
ただ,POSIXには前者の記載はあって後者はないから,
shellcheckの「後者にしろ」っていう判定は絶対におかしい。
0640デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 12:34:01.42ID:gWaB1LhD0
shellcheckは間違いだから不要と言ってるんじゃなくて、
意味がないから不要って言ってるんだよ。

お前コードチェックツールで変数が使われていませんって
指摘されたら、文法上は間違ってねーよって文句言うのかよ?
0642デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 12:36:35.34ID:gWaB1LhD0
> shellcheckの「後者にしろ」っていう判定は絶対におかしい。

後者にしろって言う判定は、前者が間違ってるって話じゃなくて、
後者もOK、だから後者はPOSIX違反ではない(POSIX違反を提案するわけがない)って話だよ

もうちょっと落ち着けよ。お前否定されたらすぐカッとなるだろw
0643デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 12:41:58.45ID:gWaB1LhD0
> つまりx=$(($x + 1))の「$x」が不要という主張は完全に間違いってことだ。
> x = $(($x - 1))とx = $((x - 1))のどっちが正しいかなんて言ってねえよ。

後者が動くのであれば
>「$x」が不要という主張は完全に間違い
という主張の方が間違ってる。

そして後者は実際に動く
0645デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 13:35:59.93ID:gWaB1LhD0
だから前者が動かないなんていてないだろ
前者に$(($x -1))の$xの$が意味がないから
(POSIX準拠で問題がない)後者の方が良いって話であって

たまにいるんだよな。誰かにより良いコードの提案をされたら
俺の書いたコードが間違っているっていうんか、仕様どおりだ!って怒るやつ
仕様違反だとか間違ってるかどうかの話なんかしてない
(正しいコードの範囲で)より良いコードにしましょうってことなのに
0646デフォルトの名無しさん (ワッチョイ 9564-uG0O)
垢版 |
2018/11/15(木) 14:51:03.70ID:sJsqi8La0
すまん。怒らせるつもりは毛頭なかったんだわ。
ただ,「標準でははこうなってますよ」と示しただけ。
まあちょっと俺の言い方もキツかったな。
言い訳させてもらうと,今までPOSIXの仕様書も読まずに
$(())算術展開は``に書き改めるべきとか,その割に[[文を使ってたりだとか
そういう人間と多く議論してきて,こっちもかなり厳密さを求めるようになってしまっていた。
正直なところ仕様違反でもほとんどの環境で動くなら,別にそっちを使ってもいいかもね。
0648デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/15(木) 15:08:37.91ID:gWaB1LhD0
$(($x - 1)) と $((x - 1)) のどちらも仕様違反じゃないのに
$(($x - 1)) が動くってことは、 $((x - 1)) は仕様違反に違いニダ!って
どういう思考回路なんだろ?

$(($x - 1)) と $((x - 1)) は前者が先に$xのパラメータ展開が行われるため動作に違いがある
https://github.com/koalaman/shellcheck/wiki/SC2004 に書いてあるとおり。
それはおそらく意図しないものだろうから警告される。

お前がPOSIXの仕様を読んでいない相手と議論してきたとか知らんが、
お前なんかよりも shellcheck の作者の方がよっぽどPOSIXの仕様に詳しいだろうよ
なにせシェルスクリプトの構文解析機を作ってるし、
世界中から間違いがあったら指摘される立場にあるんだから
0650デフォルトの名無しさん (アウアウウー Sa91-AAco)
垢版 |
2018/11/16(金) 18:15:57.23ID:4Z/2Zn+la
Windows用の busybox.exe なんてものがあることをたった今知った。
こんなのあったんだな。

まあしかしよくよく考えてみればWindows用のgccとかのコンパイラでコンパイルすれば良いだけだからさほど凄いことではないか。
0651デフォルトの名無しさん (ワッチョイ 9564-ddua)
垢版 |
2018/11/16(金) 20:52:55.20ID:0Fk/0vf10
case "$(<なにかの処理>)" in
'A'|'aaa') echo 'B' ;;
*) echo "$(<なにかの処理>)" ;;
esac
みたいな文でさ,<なにかの処理>を(できれば変数とか使わずに)一つにまとめたいんだけど
できるかな。

"$(<なにかの処理>)"の結果が'A'か'aaa'の時のみ'B'を出力して,それ以外の場合はその処理のまんまを出力したい。

RESULT="$(<なにかの処理>)"
case "$RESULT" in
'A'|'aaa') echo 'B' ;;
*) echo "$RESULT" ;;
esac
でもいいんだけどな〜んか無駄がある気がするんだよね……近い場所で二回同じ変数使ってるっていうのが。
0652デフォルトの名無しさん (アウアウウー Sa91-AAco)
垢版 |
2018/11/16(金) 21:08:59.95ID:4Z/2Zn+la
気のせい
0654デフォルトの名無しさん (ワッチョイ 3903-k2K5)
垢版 |
2018/11/16(金) 23:43:45.38ID:HodhQ/sE0
いい感じのバカっぽさw
0656デフォルトの名無しさん (ワッチョイ 3903-k2K5)
垢版 |
2018/11/17(土) 00:41:14.93ID:eQWBxdMf0
なんか悔しかったんかなw
0657デフォルトの名無しさん (ワッチョイ ca98-9Fzi)
垢版 |
2018/11/17(土) 01:07:52.12ID:LhPQgZnG0
>>651
変数を使わないの書き方の基本の一つは引数(パラメータ)を使うことだよ
引数を変数の代わりとして使う。そのやり方に2パターンある

・パターン1 関数を作る(呼び出し先の関数の引数にする)
foo() {
 case "$1" in
  'A'|'aaa') echo 'B' ;;
  *) echo "$1" ;;
 esac
}
foo "$(<なにかの処理>)"

・パターン2 引数を再設定する(自分自身の引数にする)
set -- "$(<なにかの処理>)"
case "$1" in
 'A'|'aaa') echo 'B' ;;
 *) echo "$1" ;;
esac

パターン2はこのままだと自分自身の引数がなくなってしまうが、
set -- "$(<なにかの処理>)" "$@"
とすることで、残しておくことが可能。以降は引数の番号をずらして
扱うかshiftで元に戻すなりするか、引数の個数が固定なら、
set -- "$@" "$(<なにかの処理>)" と逆にしてもよい

余談だが、そんなコードを書くぐらいなら素直に変数を使えばいいと思うかもしれないが、
シェルスクリプトの変数がグローバル変数なのに対して、
引数は(レキシカルスコープ的な)ローカル変数になってるという重要な違いがある
localやtypesetを使った変数のローカル化はダイナミックスコープなのでそれとも違う
0660デフォルトの名無しさん (ワッチョイ 56be-JwVn)
垢版 |
2018/11/17(土) 18:51:09.82ID:ikDL4PRg0
ちょっと力を貸してほしい
https://paste.ubuntu.com/p/hx6cFRKJKm/
今、↑こういう引数処理を書いている。
動作としては
$ ./argprs.sh -abc VAR -pqr FILE --long-opt --opt-req-par="foo bar" -- --not-opt
短いオプション1: a; 'VAR'
短いオプション2: b
短いオプション3: c
短いオプション4: p
短いオプション5: q
短いオプション6: r
被演算子1: FILE
長いオプション1: long-opt
長いオプション2: opt-req-par; 'foo bar'
被演算子2: --not-opt
こういう出力ができるはず。つまり-aと--opt-req-parはパラメータを取って、--以降はオプションとして解釈されないっていうやつ。
ところが俺の知識量では短いオプションを解析する部分をすごく冗長にしか書けない。いちいちループを回すせいで遅いし。
動作自体はこれでいいので、短いオプションをもうちょっとうまく解析する方法とかないかな。
0662デフォルトの名無しさん (ワッチョイ 3903-k2K5)
垢版 |
2018/11/17(土) 19:19:32.18ID:eQWBxdMf0
エクセルマクラーと同じ病気やね
0664デフォルトの名無しさん (ワッチョイ 8698-9Fzi)
垢版 |
2018/11/17(土) 20:09:39.20ID:DG9kgnl30
getoptやgetoptsは短いオプションがパラメータを取るとき、
短いオプション以降がパラメータとみなされるようだ

-abcde で cがパラメータを取るとき、
-a -b -c de と解釈される

実装によってバラバラなのは当然だろうが
こっちのほうが楽だろうな

>>660の仕様だと、aとcがパラメータを取るとき
-abcde VAR をどう解釈して良いのかわからない
-abcde VAR1 VAR2 とすりゃいいのかもしれんが、
これは人間にとってわかりやすいんだろうか?
0665デフォルトの名無しさん (ワッチョイ 3903-k2K5)
垢版 |
2018/11/17(土) 20:18:41.59ID:eQWBxdMf0
バカは考えるだけムダやぞw
■ このスレッドは過去ログ倉庫に格納されています

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