bashでの$#の意味


26

instanceという名前のファイルにスクリプトがあります。

echo "hello world"
echo ${1}

そして、次を使用してこのスクリプトを実行すると:

./instance solfish

私はこの出力を取得します:

hello world
solfish

しかし、実行すると:

echo $# 

「0」と表示されます。どうして?$#意味がわかりません。

説明してください。


10
どうして走るの$#?何を達成したいですか?このコマンドはどこで入手しましたか。それはまったく関係ありません。
パイロット6

7
この場合@ Pilot6は変数の意味を尋ねますが、これは特定のケースではないので、なぜopはその情報を与える必要があるのでしょうか?
-ADDB

21
@solfish Pilotは、あなたが期待していたことを理解しようとしています。あなたは走ったecho $#と言って、それ0は正常です。あなたはこれに驚きましたが、あなたが何を期待していたのか、なぜ驚いたのかを説明しません。それで、あなたがあなたが期待していることを説明するならば、それは我々があなたにより良い答えを与えるのを助けるでしょう。あなたはそれecho $#が何をすると思いましたか。あなたが走っ./instance solfish./instance封じ込めるならecho $#、それは印刷して1、そうではありません0
テルドン


1
Javaもargcを使用しません。
JakeRobb

回答:


66

$#はの特別な変数でbash、引数(位置パラメータ)の数に展開されます。つまり$1, $2 ...、問題のスクリプトに渡されるか、引数がシェルに直接渡される場合はシェルに渡されます(例:)bash -c '...' ....

これはargcCの場合と同様です。


おそらくこれはそれを明確にするでしょう:

$ 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を出力します。


1
この答えは誤解を招くと思います。$#は、引数の配列の最大インデックスです。あなたは一つの引数を与えた場合、それは$ 0に利用可能になるだろう、と$#次の2つの引数を与えると、彼らは$ 0、$ 1になるだろう、と$#値1になります値0を持つことになります
JakeRobb

1
さらに、を使用するbash -c場合、実行可能シェルスクリプトを実行する場合とは動作が異なります。後者の場合、インデックス0の引数は、呼び出しに使用されるシェルコマンドであるためです。したがって、この答えを修正する方法は、を使用する代わりにファイルとしてスクリプトを実行するように変更することだと思いますbash -c
JakeRobb

1
@JakeRobbいいえ$0。スクリプトの場合、スクリプト自体になります。引数はになります$1, $2, $3...bash -cそれは非対話型の使用を意図しており、コマンドに続く引数はから始まるため、動作は異なり$0ます。
heemayl

5
@JakeRobbあなたは私を誤解しました。1つの引数を指定すると、$ 0で使用可能になり、$#の値は0になります。これは明らかに間違っています。
-heemayl

2
argsを参照する完全なプログラムを配置する場合、0番目の引数のフィラーとして意味のある名前をbash -c渡す必要がbashあります。 bash -c 'echo "$@"' foo barを含めないbarため、いつもと同じように印刷します。 「$ 0を引数の1つにする」のではなくシステムコールまたは0番目の引数をオーバーライドするシェル方法でプログラムを実行するときと同じ方法で「$ 0」を設定できます。 「引数の1つ」と見なされることはありません。"$@"$0bash -cexecve$0
ピーターコーデス

17

echo $# スクリプトの位置パラメータの数を出力します。

何もないので、0を出力します。

echo $# 独立したコマンドとしてではなく、スクリプト内で便利です。

次のようないくつかのパラメータを使用してスクリプトを実行する場合

./instance par1 par2

echo $#スクリプトの意志出力2に入れました。


6
OPの例:echo $#スクリプトに行を追加してから再度実行する./instance solfish場合、11つのパラメーターを提供したため、追加の出力行を取得する必要があります
-derHugo

14

$#通常、パラメータが渡されることを保証するためにbashスクリプトで使用されます。通常、スクリプトの最初でパラメーターを確認します。

たとえば、今日私が取り組んでいたスクリプトのスニペットを次に示します。

if [[ $# -ne 1 ]]; then
    echo 'One argument required for 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]}

戻り値:(配列のゼロベースのインデックス/添え字を使用するため、0 1の長さAは最初の要素です)。


$# -ne 1?なぜそうでない、$# -le 0または同等なのか?
パトリックトレンティン

@PatrickTrentin 2つ以上のパラメーターを渡したくないので。キャプテンが「赤い10月」で言ったように:「ジャストワン」。
WinEunuuchs2Unix

次に、エラーメッセージの「正確に1つの引数が必要です...」
パトリックトレンティン

@PatrickTrentinエラーメッセージは、1つの引数の使用例とともに、スクリプトの使用方法を詳しく説明しています。さらに、バックアップスクリプトは唯一の誰かのハイテクに精通したことにより、一度に構成されているcron.dailyはによって呼び出される:askubuntu.com/questions/917562/...
WinEunuuchs2Unix

それがどのように関連するかは私にはわかりません。とにかく、乾杯。
パトリックトレンティン

10

$# は引数の数ですが、関数では異なることに注意してください。

$#は、スクリプト、シェル、またはシェル関数に渡される位置パラメーターの数です。これは、シェル関数の実行中に、位置パラメーターが一時的に関数の引数に置き換えられるためです。これにより、関数は独自の定位置パラメーターを受け入れて使用できます。

このスクリプトは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

1
求められていたものを超えた教育的回答に対する称賛は、元の質問者や私のような他の人の学習意向に応えた!
リカルド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.