当前位置: 首页>>技术教程>>正文


在 bash 中,$# 是什么意思?

问题描述

我在名为实例的文件中有一个脚本:

echo "hello world"
echo ${1}

当我使用以下命令运行此脚本时:

./instance solfish

我得到这个输出:

hello world
solfish

但是当我跑步时:

echo $# 

上面写着 “0”。为什么?我不明白 $# 是什么意思。

请解释一下。

最佳思路

$#bash 中的一个特殊变量,它扩展到参数(位置参数)的数量,即 $1, $2 ... 传递到相关脚本或 shell(如果参数直接传递到 shell),例如在 bash -c '...' .... 中。

这类似于C中的argc


也许这会清楚地表明:

$ 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。

次佳思路

echo $# 输出脚本的位置参数的数量。

你没有,所以它输出 0。

echo $# 在脚本内部很有用,而不是作为单独的命令。

如果您运行带有某些参数的脚本,例如

./instance par1 par2

放入脚本中的 echo $# 将输出 2。

第三种思路

$# 通常在 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 是第一个元素,因为数组使用从零开始的索引/下标)。

第四种思路

$# 是参数的数量,但请记住它在函数中会有所不同。

$# 是传递给脚本、shell 或 shell 函数的位置参数的数量。这是因为,当 shell 函数运行时,positional parameters are temporarily replaced with the arguments to the function .这使得函数可以接受并使用它们自己的位置参数。

无论有多少参数传递给脚本本身,此脚本始终打印 3 ,因为函数 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

参考资料

本文由Ubuntu问答整理, 博文地址: https://ubuntuqa.com/article/13785.html,未经允许,请勿转载。