linux bash Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数,如何知道Bash脚本中的脚本文件名?

在linux下配置shell参数说明

前面已经讲到,变量名只能包含数字、字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量。
例如,$ 表示当前Shell进程的ID,即pid,看下面的代码:

  1. $echo $$

运行结果

29949


特殊变量列表

变量
含义

$0
当前脚本的文件名

$n
传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。

$#
传递给脚本或函数的参数个数。

$*
传递给脚本或函数的所有参数。

$@
传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到。

$?
上个命令的退出状态,或函数的返回值。

$$
当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

命令行参数

运行脚本时传递给脚本的参数称为命令行参数。命令行参数用 $n 表示,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
请看下面的脚本:

  1. #!/bin/bash
  2. echo "File Name: $0"
  3. echo "First Parameter : $1"
  4. echo "First Parameter : $2"
  5. echo "Quoted Values: $@"
  6. echo "Quoted Values: $*"
  7. echo "Total Number of Parameters : $#"

运行结果:

$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2

$* 和 $@ 的区别

$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。
但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。
下面的例子可以清楚的看到 $* 和 $@ 的区别:

  1. #!/bin/bash
  2. echo "\$*=" $*
  3. echo "\"\$*\"=" "$*"
  4. echo "\$@=" $@
  5. echo "\"\$@\"=" "$@"
  6. echo "print each param from \$*"
  7. for var in $*
  8. do
  9. echo "$var"
  10. done
  11. echo "print each param from \$@"
  12. for var in $@
  13. do
  14. echo "$var"
  15. done
  16. echo "print each param from \"\$*\""
  17. for var in "$*"
  18. do
  19. echo "$var"
  20. done
  21. echo "print each param from \"\$@\""
  22. for var in "$@"
  23. do
  24. echo "$var"
  25. done

执行 ./test.sh "a" "b" "c" "d",看到下面的结果:

$*=  a b c d
"$*"= a b c d
$@=  a b c d
"$@"= a b c d
print each param from $*
a
b
c
d
print each param from $@
a
b
c
d
print each param from "$*"
a b c d
print each param from "$@"
a
b
c
d

退出状态

$? 可以获取上一个命令的退出状态。所谓退出状态,就是上一个命令执行后的返回结果。
退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1。
不过,也有一些命令返回其他值,表示不同类型的错误。
下面例子中,命令成功执行:

$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2
$echo $?
0
$


$? 也可以表示函数的返回值,后续将会讲解

我如何知道Bash脚本中的脚本文件名?

如何确定脚本本身内部的Bash脚本文件的名称?

就像我的脚本在文件runme.sh ,那么我如何在不进行硬编码的情况下显示“您正在运行runme.sh”消息呢?


#1楼

this="$(dirname "$(realpath "$BASH_SOURCE")")"

这样可以解析符号链接(realpath可以做到),可以处理空格(使用双引号可以做到这一点),即使在源代码(../myscript)或被其他脚本调用($ BASH_SOURCE可以处理)的情况下,也可以找到当前的脚本名称。 毕竟,最好将其保存在环境变量中以供重用或在其他位置轻松复制(this =)...


#2楼

me=`basename "$0"`

要通读通常不是您想要的符号链接1 (您通常不希望这样使用户感到困惑),请尝试:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

海事组织,这将产生令人困惑的输出。 “我运行了foo.sh,但这是说我正在运行bar.sh !?一定是bug!” 此外,具有不同名称的符号链接的目的之一是根据它的名称提供不同的功能(在某些平台上可以考虑一下gzip和gunzip)。


1也就是说,要解析符号链接,以便当用户执行foo.sh (实际上是bar.sh的符号链接)时,您希望使用解析的名称bar.sh而不是foo.sh


#3楼

echo“您正在运行$ 0”


#4楼

您可以使用$ 0来确定脚本名称(具有完整路径)-仅获取脚本名称,您可以使用

basename $0

#5楼

如果脚本名称中包含空格,则更健壮的方法是使用"$0""$(basename "$0")" -或在MacOS上使用"$(basename \\"$0\\")" 。 这样可以防止以任何方式混淆或解释该名称。 通常,良好的做法是始终在外壳程序中双引号。


#6楼

$0不能回答问题(据我了解)。 演示:

$ cat script.sh
#! /bin/sh
echo `basename $0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

如何获得./linktoscript以打印出script.sh ?

[编辑]在上面的注释中,尽管@ephemient似乎是虚构的,但仍可能摆弄$0使其不表示文件系统资源。 OP对他想要的内容有点含糊。


#7楼

要回答Chris Conway ,至少在Linux上,您可以这样做:

echo $(basename $(readlink -nf $0))

readlink打印出符号链接的值。 如果不是符号链接,则打印文件名。 -n告诉它不打印换行符。 -f告诉它完全跟随该链接(如果符号链接是到另一个链接的链接,则也会解析该链接)。


#8楼

如果您希望它没有路径,则可以使用${0##*/}


#9楼

我发现此行始终有效,无论文件是源文件还是作为脚本运行。

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

如果要遵循符号链接,请在您到达的路径上以递归或非递归方式使用readlink 。

通过使用BASH_SOURCE环境变量及其关联的FUNCNAME来解释单行工作的原因。

BASH_SOURCE

一个数组变量,其成员是源文件名,在其中定义了FUNCNAME数组变量中的相应外壳函数名称。 外壳函数$ {FUNCNAME [$ i]}在文件$ {BASH_SOURCE [$ i]}中定义,并从$ {BASH_SOURCE [$ i + 1]}中调用。

功能名称

一个数组变量,包含当前在执行调用堆栈中的所有shell函数的名称。 索引为0的元素是任何当前正在执行的Shell函数的名称。 最底部的元素(具有最高索引的元素)是“ main”。 仅当执行Shell函数时,此变量才存在。 分配给FUNCNAME无效,并返回错误状态。 如果未设置FUNCNAME,则即使随后将其重置,它也会失去其特殊属性。

此变量可以与BASH_LINENO和BASH_SOURCE一起使用。 FUNCNAME的每个元素在BASH_LINENO和BASH_SOURCE中都有相应的元素来描述调用堆栈。 例如,从文件$ {BASH_SOURCE [$ i + 1]}在行号$ {BASH_LINENO [$ i]}处调用了$ {FUNCNAME [$ i]}。 内置呼叫方使用此信息显示当前呼叫堆栈。

[来源:Bash手册]


#10楼

信息感谢Bill Hernandez。 我添加了一些采用的偏好设置。

  1. #!/bin/bash
  2. function Usage(){
  3. echo " Usage: show_parameters [ arg1 ][ arg2 ]"
  4. }
  5. [[ ${#2} -eq 0 ]] && Usage || {
  6. echo
  7. echo "# arguments called with ----> ${@} "
  8. echo "# \$1 -----------------------> $1 "
  9. echo "# \$2 -----------------------> $2 "
  10. echo "# path to me ---------------> ${0} " | sed "s/$USER/\$USER/g"
  11. echo "# parent path --------------> ${0%/*} " | sed "s/$USER/\$USER/g"
  12. echo "# my name ------------------> ${0##*/} "
  13. echo
  14. }

干杯


#11楼

这样的东西?

  1. export LC_ALL=en_US.UTF-8
  2. #!/bin/bash
  3. #!/bin/sh
  4.  
  5. #----------------------------------------------------------------------
  6. start_trash(){
  7. ver="htrash.sh v0.0.4"
  8. $TRASH_DIR # url to trash $MY_USER
  9. $TRASH_SIZE # Show Trash Folder Size
  10.  
  11. echo "Would you like to empty Trash [y/n]?"
  12. read ans
  13. if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
  14. then
  15. echo "'yes'"
  16. cd $TRASH_DIR && $EMPTY_TRASH
  17. fi
  18. if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
  19. then
  20. echo "'no'"
  21. fi
  22. return $TRUE
  23. }
  24. #-----------------------------------------------------------------------
  25.  
  26. start_help(){
  27. echo "HELP COMMANDS-----------------------------"
  28. echo "htest www open a homepage "
  29. echo "htest trash empty trash "
  30. return $TRUE
  31. } #end Help
  32. #-----------------------------------------------#
  33.  
  34. homepage=""
  35.  
  36. return $TRUE
  37. } #end cpdebtemp
  38.  
  39. # -Case start
  40. # if no command line arg given
  41. # set val to Unknown
  42. if [ -z $1 ]
  43. then
  44. val="*** Unknown ***"
  45. elif [ -n $1 ]
  46. then
  47. # otherwise make first arg as val
  48. val=$1
  49. fi
  50. # use case statement to make decision for rental
  51. case $val in
  52. "trash") start_trash ;;
  53. "help") start_help ;;
  54. "www") firefox $homepage ;;
  55. *) echo "Sorry, I can not get a $val for you!";;
  56. esac
  57. # Case stop

#12楼

# ------------- SCRIPT ------------- #
  1.  
  2. #!/bin/bash
  3.  
  4. echo
  5. echo "# arguments called with ----> ${@} "
  6. echo "# \$1 ----------------------> $1 "
  7. echo "# \$2 ----------------------> $2 "
  8. echo "# path to me ---------------> ${0} "
  9. echo "# parent path --------------> ${0%/*} "
  10. echo "# my name ------------------> ${0##*/} "
  11. echo
  12. exit

# ------------- CALLED ------------- #

# Notice on the next line, the first argument is called within double, 
# and single quotes, since it contains two words

$  /misc/shell_scripts/check_root/show_parms.sh "'hello there'" "'william'"

# ------------- RESULTS ------------- #

# arguments called with --->  'hello there' 'william'
# $1 ---------------------->  'hello there'
# $2 ---------------------->  'william'
# path to me -------------->  /misc/shell_scripts/check_root/show_parms.sh
# parent path ------------->  /misc/shell_scripts/check_root
# my name ----------------->  show_parms.sh

# ------------- END ------------- #

#13楼

回复:上面的Tanktalus(已接受)答案,一种更简洁的方法是使用:

me=$(readlink --canonicalize --no-newline $0)

如果您的脚本来自另一个bash脚本,则可以使用:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

我同意,如果您的目标是向用户提供反馈,则取消引用符号链接会令人困惑,但是有时候您确实需要将规范名称获取到脚本或其他文件,这是imo的最佳方法。


#14楼

如果您的调用shell脚本像

/home/mike/runme.sh

$ 0是全名

 /home/mike/runme.sh

basename $ 0将获得基本文件名

 runme.sh

并且您需要将此基本名称放入类似的变量中

filename=$(basename $0)

并添加其他文字

echo "You are running $filename"

所以你的脚本就像

  1. /home/mike/runme.sh
  2. #!/bin/bash
  3. filename=$(basename $0)
  4. echo "You are running $filename"

#15楼

  1. echo "$(basename "`test -L ${BASH_SOURCE[0]} \
  2. && readlink ${BASH_SOURCE[0]} \
  3. || echo ${BASH_SOURCE[0]}`")"

#16楼

bash您可以使用$0获得脚本文件名。 通常$1 , $2等用于访问CLI参数。 同样, $0用于访问触发脚本的名称(脚本文件名)。

  1. #!/bin/bash
  2. echo "You are running $0"
  3. ...
  4. ...

如果您使用/path/to/script.sh之类的/path/to/script.sh调用脚本,则$0还将提供带有路径的文件名。 在这种情况下,需要使用$(basename $0)仅获取脚本文件名。


#17楼

由于一些评论询问了不带扩展名的文件名,因此,下面是一个示例,说明了如何完成该操作:

  1. FileName=${0##*/}
  2. FileNameWithoutExtension=${FileName%.*}

请享用!


#18楼

这是我根据Dimitre Radoulov的回答(顺便说一句,我赞成)的启发得出的。

  1. script="$BASH_SOURCE"
  2. [ -z "$BASH_SOURCE" ] && script="$0"
  3.  
  4. echo "Called $script with $# argument(s)"

无论您如何调用脚本

. path/to/script.sh

要么

./path/to/script.sh

#19楼

my_script.sh简短,清晰,简单

  1. #!/bin/bash
  2.  
  3. running_file_name=$(basename "$0")
  4.  
  5. echo "You are running '$running_file_name' file."

输出:

  1. ./my_script.sh
  2. You are running 'my_script.sh' file.

#20楼

DIRECTORY=$(cd `dirname $0` && pwd)

我从另一个堆栈溢出问题得到了上述内容, 一个Bash脚本可以告诉它存储在哪个目录中吗? ,但我认为这对于本主题也很有用。


#21楼

在采购脚本时, $BASH_SOURCE提供正确的答案。

但是,这包括路径,因此仅获取脚本文件名,请使用:

$(basename $BASH_SOURCE) 

#22楼

这些答案对于所陈述的情况是正确的,但是如果您使用'source'关键字从另一个脚本运行该脚本(以使其在同一外壳中运行),则仍然存在问题。 在这种情况下,您将获得调用脚本的$ 0。 在这种情况下,我认为不可能获得脚本本身的名称。

这是一个极端的情况,不应太当真。 如果直接从另一个脚本(不带“源”)运行该脚本,则可以使用$ 0。


#23楼

使用bash> = 3时 ,以下工作有效:

  1. $ ./s
  2. 0 is: ./s
  3. BASH_SOURCE is: ./s
  4. $ . ./s
  5. 0 is: bash
  6. BASH_SOURCE is: ./s
  7.  
  8. $ cat s
  9. #!/bin/bash
  10.  
  11. printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"
posted @ 2021-04-16 16:28  CharyGao  阅读(98)  评论(0)    收藏  举报