我在名为实例的文件中有一个脚本:
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 函数。这是因为,当 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