$#在bash中是什么意思?


26

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

echo "hello world"
echo ${1}

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

./instance solfish

我得到以下输出:

hello world
solfish

但是当我跑步时:

echo $# 

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

请解释一下。


10
你为什么跑$#?您想实现什么?您从何处获得此命令。它根本不相关。
飞行员

7
@ Pilot6在这种情况下,他询问变量的含义,这不是大小写特定的,那么为什么op需要提供该信息?
ADDB

21
@solfish飞行员正在尝试了解您的期望。您说您跑步了echo $#,它返回了0,这很正常。您对此感到惊讶,但是您没有解释自己的期望或为什么感到惊讶。因此,如果您解释了自己的期望,这将有助于我们为您提供更好的答案。您认为echo $#那样做。如果运行./instance solfish./instance包含echo $#,将打印1,而不是0
terdon


1
Java也不使用argc。
JakeRobb '17

Answers:


66

$#是in中的一个特殊变量bash,它扩展为参数(位置参数)的数量,即$1, $2 ...,如果参数直接传递给外壳,例如in中,则传递给相关脚本或外壳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,$#的值将是0。如果提供两个参数,它们会在$ 0 $ 1,$和#将具有值1
JakeRobb

1
此外,使用时bash -c,行为与运行可执行的Shell脚本的行为不同,因为在后一种情况下,索引为0的参数是用于调用它的Shell命令。因此,我认为解决此问题的方法是将其更改为将脚本作为文件执行,而不是使用bash -c,因为这是问问者的做法。
JakeRobb '17

1
@JakeRobb否。对于脚本而言,$0将是脚本本身。论据是$1, $2, $3...bash -c行为是不同的,因为它是用于非交互使用的,并且命令后的参数将从开头$0,我相信我已经明确提到了这一点。
heemayl

5
@JakeRobb你听错了我的话。如果您给出一个参数,它将在$ 0处可用,并且$#的值为0-这是完全错误的。
heemayl

2
如果将完整的程序放在args中bash -c,则应传递bash一些有意义的名称作为第0个arg的填充符。 bash -c 'echo "$@"' foo bar仅打印bar,因为"$@"不包含$0,与以往一样。 bash -c不“将$ 0 args来一个”,它只是允许你设置“$ 0”运行一个程序,当你以同样的方式execve系统调用或重写第0 arg的任何壳方式。 $0永远不应被视为“ args之一”。
彼得·科德斯

17

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

您一无所有,因此输出为0。

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

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

./instance par1 par2

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


6
对于OP的示例:如果将行添加echo $#到脚本中,然后再次运行,则./instance solfish应该提供一条附加输出行,1因为您提供了1个参数
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]}

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


$# -ne 1?为什么不$# -le 0相等?
帕特里克·特伦汀

@PatrickTrentin因为我不希望它们传递两个或多个参数。正如机长在“红色十月”中所说:“只是一个”。
WinEunuuchs2Unix

然后:错误消息中“只需要一个参数...”
Patrick Trentin

@PatrickTrentin该错误消息通过一个参数用法示例说明了如何使用脚本。此外,备份脚本由cron.daily调用,该脚本仅由精通技术的人配置一次:askubuntu.com/questions/917562/…–
WinEunuuchs2Unix

我不知道这有多重要。无论如何,欢呼。
帕特里克·特伦汀

10

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

$#是传递给脚本,shell 或shell函数的位置参数的数量。这是因为在运行Shell函数时,位置参数会临时替换为该函数的参数。这使函数可以接受并使用自己的位置参数。

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

1
荣誉的教育答案超出了要求,但对原始提问者和其他类似我的学习意图做出了回应!
里卡多
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.