$# 在 bash 中是什么意思?

sol*_*ish 45 bash

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

echo "hello world"
echo ${1}
Run Code Online (Sandbox Code Playgroud)

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

./instance solfish
Run Code Online (Sandbox Code Playgroud)

我得到这个输出:

hello world
solfish
Run Code Online (Sandbox Code Playgroud)

但是当我运行时:

echo $# 
Run Code Online (Sandbox Code Playgroud)

它说“0”。为什么?我不明白什么$#意思。

请解释一下。

hee*_*ayl 90

$#是一个特殊变量 in bash,它扩展到参数(位置参数)的数量,即$1, $2 ...传递给有问题的脚本或在参数直接传递给 shell 的情况下的 shell,例如 in 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
Run Code Online (Sandbox Code Playgroud)

请注意,bash -c在从 0 开始的命令之后接受参数($0;从技术上讲,这只是bash让您设置的方式$0,而不是真正的参数),因此_在此处仅用作占位符;实际参数是x( $1)、y( $2) 和z( $3)。


同样,在您的脚本中(假设script.sh),如果您有:

#!/usr/bin/env bash
echo "$#"
Run Code Online (Sandbox Code Playgroud)

然后当你这样做时:

./script.sh foo bar
Run Code Online (Sandbox Code Playgroud)

脚本将输出 2;同样地,

./script.sh foo
Run Code Online (Sandbox Code Playgroud)

将输出 1。

  • @JakeRobb 你听错了。_如果你给出一个参数,它的价格为 $0,而 $# 的值为 0_——这是完全错误的。 (5认同)
  • 此外,当您使用“bash -c”时,行为与运行可执行 shell 脚本不同,因为在后一种情况下,索引为 0 的参数是用于调用它的 shell 命令。因此,我认为解决这个答案的方法是将其更改为以文件形式执行脚本,而不是使用“bash -c”,因为提问者就是这样做的。 (3认同)
  • @JakeRobb 不。对于脚本,“$0”将是脚本本身;参数为“$1, $2, $3...”。`bash -c` 的行为是不同的,因为它用于非交互式使用,并且命令后面的参数将从 `$0` 开始,我相信我已经清楚地提到了这一点。 (3认同)
  • 我认为这个答案具有误导性。$# 是参数数组的最大索引。如果你给出一个参数,它的价值为 $0,而 $# 的值为 0。如果你给出两个参数,它们将在 $0 和 $1 中,而 $# 的值为 1。 (2认同)
  • 如果你把一个完整的程序放在 `bash -c` 中查看它的参数,你应该给 `bash` 传递一些有意义的名字作为第 0 个参数的填充符。`bash -c 'echo "$@"' foo bar` 只打印 `bar`,因为 `"$@"` 不包含 `$0`,和往常一样。`bash -c` 不会“使 $0 成为参数之一”,它仅允许您在使用 [`execve` 系统调用](http://man7. org/linux/man-pages/man2/execve.2.html) 或任何覆盖第 0 个参数的 shell 方式。`$0` 永远不应被视为“参数之一”。 (2认同)

Pil*_*ot6 17

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

你没有,所以它输出 0。

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

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

./instance par1 par2
Run Code Online (Sandbox Code Playgroud)

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

  • 对于 OP 的示例:如果将 `echo $#` 行添加到脚本中,然后再次运行 `./instance solfish`,你应该得到一个带有 `1` 的额外输出行,因为你提供了 1 个参数 (6认同)

Win*_*nix 15

$#通常用于 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
Run Code Online (Sandbox Code Playgroud)

总结$#报告传递给脚本的参数数量。在您的情况下,您没有传递任何参数,报告的结果是0.


#Bash 中的其他用途

#通常在bash用于计数出现的次数或可变的长度。

要查找字符串的长度:

myvar="some string"; echo ${#myvar}
Run Code Online (Sandbox Code Playgroud)

返回: 11

要查找数组元素的数量:

myArr=(A B C); echo ${#myArr[@]}
Run Code Online (Sandbox Code Playgroud)

返回: 3

要查找第一个数组元素的长度:

myArr=(A B C); echo ${#myArr[0]}
Run Code Online (Sandbox Code Playgroud)

返回:(, 01的长度A是第一个元素,因为数组使用从零开始的索引/下标)。


Eli*_*gan 11

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

$#是传递给脚本、shell或 shell 函数的位置参数的数量。这是因为,当 shell 函数运行时,位置参数会临时替换为函数的参数。这让函数接受并使用它们自己的位置参数。

3无论有多少参数传递给脚本本身,该脚本始终打印,因为"$#"在函数中f扩展为传递给函数的参数数量:

#!/bin/sh

f() {
    echo "$#"
}

f a b c
Run Code Online (Sandbox Code Playgroud)

这很重要,因为这意味着如果您不熟悉位置参数在 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...
Run Code Online (Sandbox Code Playgroud)

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 "$#"
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为在函数之外$#扩展并作为位置参数之一传递给函数。在函数内部,扩展到传递给 shell 函数的第一个位置参数,而不是它所属的脚本。$1

因此,像$#,特殊参数$1$2等等,以及$@$*,也涉及到传递给函数的参数,当他们在功能被扩展。但是,$0不能更改的功能,它的名称是为什么我仍然能够用它来生产高质量的错误消息。

$ ./check-args-demo a b c
./check-args-demo: error: need 2 arguments, got 3
Run Code Online (Sandbox Code Playgroud)

类似地,如果您在另一个函数中定义一个函数,则您正在使用传递给执行扩展的最内部函数的位置参数:

#!/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
Run Code Online (Sandbox Code Playgroud)

我调用了这个脚本nested并且(在运行之后chmod +x nested)我运行了它:

$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments
Run Code Online (Sandbox Code Playgroud)

是的我知道。“1 个参数”是一个复数错误。

位置参数也可以改变。

如果您正在编写脚本,则函数外部的位置参数将是传递给脚本的命令行参数,除非您对其进行了更改

改变它们的一种常见方法是使用shift内置函数,它将每个位置参数向左移动一个,删除第一个并减少$#1:

#!/bin/sh

while [ "$#"  -ne 0 ]; do
    printf '%d argument(s) remaining.\nGot "%s".\n\n' "$#" "$1"
    shift
done
Run Code Online (Sandbox Code Playgroud)
$ ./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".
Run Code Online (Sandbox Code Playgroud)

它们也可以使用set内置函数进行更改:

#!/bin/sh

printf '%d args: %s\n' "$#" "$*"
set foo bar baz
printf '%d args: %s\n' "$#" "$*"
Run Code Online (Sandbox Code Playgroud)
$ ./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
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的教育性答案,它超出了所要求的范围,但回应了最初提问者和像我这样的其他人的学习意图! (2认同)