
インスタンスという名前のファイルにスクリプトがあります:
echo "hello world"
echo ${1}
そして、このスクリプトを次のように実行します:
./instance solfish
次のような出力が得られます:
hello world
solfish
しかし、実行すると:
echo $#
「0」と表示されています。なぜですか? 意味がわかりません$#
。
説明してください。
答え1
$#
は 内の特殊変数でbash
、引数 (位置パラメータ) の数に展開されます。つまり、$1, $2 ...
問題のスクリプトに渡される引数の数、または など、引数がシェルに直接渡される場合はシェルに渡されますbash -c '...' ....
。
argc
これはC の場合と同様です。
おそらく、次のことが明確になるでしょう:
$ bash -c 'echo $#'
0
$ bash -c 'echo $#' _ x
1
$ bash -c 'echo $#' _ x y
2
$ bash -c 'echo $#' _ x y z
3
は、bash -c
コマンドの後に 0 から始まる引数を取ることに注意してください ( $0
; 技術的には、これbash
は を設定するための の方法であり$0
、実際には引数ではありません)。したがって、ここでは は単なるプレースホルダーとして使用されています。実際の引数は( )、( )、および( )_
です。x
$1
y
$2
z
$3
同様に、スクリプトでは( と仮定してscript.sh
)、次のようになります。
#!/usr/bin/env bash
echo "$#"
次に、次の操作を実行します。
./script.sh foo bar
スクリプトは2を出力します。同様に、
./script.sh foo
1 を出力します。
答え2
echo $#
スクリプトの位置パラメータの数を出力します。
何もないので、0 が出力されます。
echo $#
個別のコマンドとしてではなく、スクリプト内で役立ちます。
次のようなパラメータを指定してスクリプトを実行すると、
./instance par1 par2
echo $#
スクリプト内に配置されたものは2 を出力します。
答え3
$#
は、通常、bash スクリプトでパラメータが渡されることを確認するために使用されます。一般的に、スクリプトの先頭でパラメータをチェックします。
たとえば、今日私が作業していたスクリプトの一部を以下に示します。
if [[ $# -ne 1 ]]; then
echo 'One argument required for the file name, e.g. "Backup-2017-07-25"'
echo '.tar will automatically be added as a file extension'
exit 1
fi
スクリプトに渡されたパラメータの数を要約して$#
報告します。この場合、パラメータは渡されず、報告された結果は です0
。
#
Bashでのその他の用途
は、#
変数の出現回数や長さをカウントするために bash でよく使用されます。
文字列の長さを調べるには:
myvar="some string"; echo ${#myvar}
戻り値:11
配列要素の数を調べるには:
myArr=(A B C); echo ${#myArr[@]}
戻り値:3
最初の配列要素の長さを見つけるには:
myArr=(A B C); echo ${#myArr[0]}
戻り値: 1
( の長さA
。配列はゼロベースのインデックス/添字を使用するため、0 は最初の要素です)。
答え4
$#
は引数の数ですが、関数では異なることに注意してください。
$#
スクリプト、シェルに渡される位置パラメータの数です。またはシェル関数これは、シェル関数の実行中に、位置パラメータは関数の引数に一時的に置き換えられますこれにより、関数は独自の位置パラメータを受け入れて使用できるようになります。
このスクリプトは3
、スクリプト自体に渡された引数の数に関係なく、常に を出力します"$#"
。これは、f
関数 が関数に渡された引数の数に展開されるためです。
#!/bin/sh
f() {
echo "$#"
}
f a b c
これは、シェル関数で位置パラメータがどのように機能するかをよく知らない場合、次のようなコードが期待どおりに機能しないことを意味するため重要です。
#!/bin/sh
check_args() { # doesn't work!
if [ "$#" -ne 2 ]; then
printf '%s: error: need 2 arguments, got %d\n' "$0" "$#" >&2
exit 1
fi
}
# Maybe check some other things...
check_args
# Do other stuff...
ではcheck_args
、$#
関数自体に渡される引数の数に展開されます。このスクリプトでは常に 0 になります。
シェル関数でこのような機能が必要な場合は、代わりに次のように記述する必要があります。
#!/bin/sh
check_args() { # works -- the caller must pass the number of arguments received
if [ "$1" -ne 2 ]; then
printf '%s: error: need 2 arguments, got %d\n' "$0" "$1" >&2
exit 1
fi
}
# Maybe check some other things...
check_args "$#"
$#
これは拡張されているため機能します外関数に渡されるのは、その位置パラメータ。関数内では、$1
その一部であるスクリプトではなく、シェル関数に渡された最初の位置パラメータに展開されます。
したがって、 と同様に$#
、特殊パラメータ$1
、$2
など、および も$@
、$*
関数内で展開されるときに関数に渡される引数に関係します。ただし$0
、ない関数の名前を変更したので、それを使用して高品質のエラー メッセージを生成することができました。
$ ./check-args-demo a b c
./check-args-demo: error: need 2 arguments, got 3
同様に、ある関数を別の関数内で定義する場合は、展開が実行される最も内側の関数に渡される位置パラメータを使用します。
#!/bin/sh
outer() {
inner() {
printf 'inner() got %d arguments\n' "$#"
}
printf 'outer() got %d arguments\n' "$#"
inner x y z
}
printf 'script got %d arguments\n' "$#"
outer p q
私はこのスクリプトを呼び出しnested
、( を実行した後chmod +x nested
)それを実行しました:
$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments
はい、わかっています。「1 引数」は複数形化のバグです。
位置パラメータも変更できます。
スクリプトを書いている場合、関数外の位置パラメータはスクリプトに渡されるコマンドライン引数になります。変更しない限り。
これらを変更する一般的な方法の 1 つは、shift
組み込み関数を使用することです。組み込み関数は、各位置パラメータを 1 つ左にシフトし、最初のパラメータを削除して$#
1 つ減らします。
#!/bin/sh
while [ "$#" -ne 0 ]; do
printf '%d argument(s) remaining.\nGot "%s".\n\n' "$#" "$1"
shift
done
$ ./do-shift foo bar baz # I named the script do-shift.
3 argument(s) remaining.
Got "foo".
2 argument(s) remaining.
Got "bar".
1 argument(s) remaining.
Got "baz".
組み込み関数を使用して変更することもできますset
:
#!/bin/sh
printf '%d args: %s\n' "$#" "$*"
set foo bar baz
printf '%d args: %s\n' "$#" "$*"
$ ./set-args a b c d e # I named the script set-args.
5 args: a b c d e
3 args: foo bar baz