bash 中的 $# 是什麼意思?

bash 中的 $# 是什麼意思?

我在名為實例的檔案中有一個腳本:

echo "hello world"
echo ${1}

當我使用以下命令運行此腳本時:

./instance solfish

我得到這個輸出:

hello world
solfish

但是當我跑步時:

echo $# 

它說“0”。為什麼?我不明白什麼$#意思。

請解釋一下。

答案1

$#是 中的一個特殊變量bash,它擴展為參數(位置參數)的數量,即$1, $2 ...傳遞到相關腳本或 shell(如果參數直接傳遞到 shell,例如 中)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

$#是參數的數量,但請記住它在函數中會有所不同。

$#是傳遞給腳本、shell 的位置參數的數量,或外殼函數。這是因為,當 shell 函數運行時,位置參數暫時替換為函數的參數。這使得函數可以接受並使用它們自己的位置參數。

3無論傳遞給腳本本身的參數數量如何,此腳本始終會列印,因為"$#"in 函數f會擴展為傳遞給函數的參數數量:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

這很重要,因為如果您不熟悉位置參數在 shell 函數中的工作方式,這意味著這樣的程式碼將無法按您的預期工作:

#!/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。

如果您想在 shell 函數中使用此類功能,則必須編寫如下內容:

#!/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擴展為傳遞給 shell 函數的第一個位置參數,而不是傳遞給它所屬的腳本。

因此,像、 、等$#特殊參數以及 和 一樣,當它們在函數中展開時,它們也屬於傳遞給函數的參數。然而,確實$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 個參數」是一個複數錯誤。

位置參數也可以變更。

如果您正在編寫腳本,函數外部的位置參數將是傳遞給腳本的命令列參數除非你改變了它們

更改它們的常見方法是使用shift內建函數,它將每個位置參數向左移動一位,刪除第一個參數並減少$#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

相關內容