web-dev-qa-db-ja.com

ソーススクリプトに渡されたパラメーターが間違っている

渡されたパラメーターの数をチェックし、パラメーターが正しくない場合は文句を言う、AIX上のスクリプト:-

if [ "$#" -ge 1 ]; then
  ...
else
  echo 'Usage: myscript <a> [b] [c]'
fi

スクリプトは、ソースとなるようにいくつかの環境変数を設定します。つまり、コマンドラインで次のように入力します。 2番目のスクリプトはありません。別のスクリプト内からスクリプトを調達していません。

. ./myscript.sh

パラメータを渡さない場合は、usageステートメントが出力されると思いますが、代わりに、ソースとなった前のコマンドに渡されたパラメータを使用して実行されます(必ずしもこのスクリプトである必要はありません!)。

これを再確認するために、スクリプトの最初にこの行を追加して、これが事実であることを証明しました。

echo $# $@

パラメータを渡さないと、以前にソースされたスクリプトからパラメータの数とリストが出力されます。

どうすれば$#にゼロのパラメーターを渡してソースすると、ゼロになりますか?

したがって、可能な最小の再作成はスクリプトです:-

myscript.sh

echo $# $@

そしてそれをこうして実行します:-

. ./myscript.sh a b c

印刷する

3 a b c

次に、パラメータなしで再度実行します。-

. ./myscript.sh

同じを出力します

3 a b c

あなたがそれを印刷することを期待するとき

0

スクリプトを入手せず、次のように実行する場合:-

./myscript.sh

その後、期待どおりに動作し、印刷されます

0

それが当面の問題を明らかにすることを願っています。

上記の方法がうまくいかない理由を説明するための多くの回答と、代わりに何をすべきかを説明するための回答が得られないようです。

要件

少なくとも1つのパラメーターを予期し、そのパラメーターで呼び出されないと文句を言うスクリプトを作成する必要があり(上記の質問の最初のコードブロックを参照)、このスクリプトはいくつかの環境変数を設定する必要があります。この2番目の要件のため、スクリプトのソースを作成して、スクリプトの完了時に環境変数が設定されたままになるようにする必要があると想定しました。環境変数を設定することもできるパラメーターが渡されていないことを通知できるスクリプトをどのように作成できますか?

4
Morag Hughson

. path/to/script at least one argument

他の人が言っているように、シェルに応じて、これらの余分な引数は無視されるか(Bourne、Almquist)、現在の位置パラメータのセットが置き換えられます($@$1$2.。 )そのスクリプト(他のほとんどのシェル)の解釈。 POSIXは、追加の引数が.に渡されたときの動作を指定しません。その場合、スクリプト内で行われた位置パラメーターへの変更(set a bなど)が.の終了後に残るかどうかについても、シェル間で多少の違いがあります。

に:

. path/to/script

ただし、動作は指定されています。位置パラメータは影響を受けるために必要ですnotなので、スクリプトの解釈のために以前と同じままです。

スクリプトの解釈中に定位置パラメーターを空のリストにしたい場合は、事前にリセットするか、関数を使用する必要があります。

set -- # or shift "$#"
. path/to/script

(位置パラメータは失われますが)。または、次の関数を使用します。

dot() { file=$1; shift; . "$file"; }
dot path/to/myscript

.コマンドが実行されるとき、位置パラメータは関数の位置パラメータになります。 shiftの後は、関数に渡される引数から最初の引数を引いたものになるため、上記では空のリストになります。ただし、この関数を使用すると、次のように、位置パラメータの任意のリストをソーススクリプトに移植可能に渡すことができます。

dot path/to/myscript extra args

これらのextraargsは、ソーススクリプト内で$1および$2として使用できます。

また、myscriptsetを呼び出して位置パラメーターのリストを変更した場合、それは関数の位置パラメーターにのみ影響し、関数が戻った後もそれらの変更は持続しません。

3

.のBashのヘルプテキスト:

.: . filename [arguments]
    Execute commands from a file in the current Shell.

    Read and execute commands from FILENAME in the current Shell.  The
    entries in $PATH are used to find the directory containing FILENAME.
    If any ARGUMENTS are supplied, they become the positional parameters
    when FILENAME is executed.

if引数が指定された」という表現に注意してください。引数がない場合、現在の位置パラメータはそのままです。

$ cat /tmp/sourceme.sh     
printf "%d: " "$#"
printf "<%s> " "$@"
printf "\n"
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh'
2: <aa> <bb> 
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <foo> <bar> 

私がテストした他のすべてのシェル(zsh、ksh)も同様に動作します。 dashを除いて、alwaysは元の位置パラメータをそのまま残します。

$ dash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <aa> <bb> 

.のPOSIX定義 で位置パラメータをリセットすることについての言及が見当たらないので、.に引数を与えることはそもそも非標準のようです。


ソーススクリプトの代わりに、標準のシェルで引数を受け入れることができる関数を使用できます。この場合、関数を定義するファイルを入手します。

例えば。関数定義を持つファイル(define_fun.sh):

#!/bin/sh
f() {
    echo "args: $# $@"     # just a test
    if [ "$#" = 0 ]; then 
        echo please give at least one argument
        return 1
    fi;
    ENVVAR=$1          # this should be visible to the whole Shell  
    export ENVVAR      # and any commands called later
} 

そしてそれを使います:

$ . define_fun.sh      # source the file to load the func in the current Shell

$ f                    # actually run it: this'll complain
$ f 123 456            # this should set ENVVAR
$ echo $ENVVAR         # and this will print '123'

代わりに、関数定義をシェルの初期化ファイルの1つに配置することもできます(~/.profile?はシェルによって異なります)。

2
ilkkachu

dot組み込みコマンドに引数を渡すことは、他のシェルでの動作が許可されていないKornShell拡張機能です。

POSIX標準ではdotコマンドの$#と$ @について言及されていないため、現在のシェルへの引数はdotコマンドに表示されます。たとえば、シェルを呼び出すと、なので:

myshell -s a b c

その後、

. ./myscript.sh

これは印刷されると予想されます

3 a b c
0
schily