我正在上 Linux Essentials 课程,在学习脚本章节之前,我学得还不错。我就是不懂这些概念。想知道是否有人可以将以下内容分解成极其简单的术语,或者为我提供更好的参考资料来学习它。我目前正在使用 netacad 的课程。
摘自教科书(格式略有改动):
除了您设置的变量外,还有一些特殊变量。您可以将参数传递给脚本:
#!/bin/bash echo "Hello $1"
美元符号后跟数字 N 对应于传递给脚本的第 N 个参数。如果您使用上面的示例调用,输出
./test.sh
将是 Hello Linux。$0
变量包含脚本本身的名称。程序运行后,无论是二进制还是脚本,它都会返回一个退出代码,该代码是 0 到 255 之间的整数。您可以通过变量测试它,
$?
以查看上一个命令是否成功完成。
我知道如何分配变量以及它们如何与一起工作,$
但整个问题$0
——$1
我只是不明白。
任何帮助将非常感激。
答案1
书中的描述是错误的(或者至少缺少了一些东西)。要让该脚本打印“Hello Linux”,您可以像这样运行它:
./test.sh Linux
如果仅使用 运行它./test.sh
,则它只会打印“Hello”,因为没有第一个参数并且$1
未定义。另一方面,假设您像这样运行它:
./test.sh foo bar baz
然后在脚本中,$0
将是“。/test.sh”,$1
将是“foo”,$2
将是“bar”,$3
将是“baz”。
至于$?
,请考虑以下脚本片段:
ls nonexistentfile.txt
echo "The exit status of ls was: $?"
echo "The exit status of echo (the first one) was: $?"
运行时会打印类似如下的内容:
ls: nonexistentfile.txt: No such file or directory
The exit status of ls was: 1
The exit status of echo (the first one) was: 0
该ls
命令无法列出 nonexistentfile.txt(因为它不存在),因此它会打印一条错误消息,并以非零状态退出以表明出现问题。第一个echo
命令打印该退出状态($?
),由于它成功执行了该操作,因此它以零状态退出。当第二个echo
命令运行时,它会$?
从第一个echo
命令中获取,因此它会打印“0”。
顺便说一句,很多命令只使用退出状态 0(成功)或 1(某种失败),但有些命令使用不同的失败状态来准确指示出了什么问题。以下是curl
手册页:
EXIT CODES
There are a bunch of different error codes and their corresponding
error messages that may appear during bad conditions. At the time of
this writing, the exit codes are:
1 Unsupported protocol. This build of curl has no support for this
protocol.
2 Failed to initialize.
3 URL malformed. The syntax was not correct.
...
88 FTP chunk callback reported error
89 No connection available, the session will be queued
90 SSL public key does not matched pinned public key
91 Invalid SSL certificate status.
92 Stream error in HTTP/2 framing layer.
...因此,使用的脚本curl
可以检查$?
出哪里出了问题,并根据问题做出不同的响应。
答案2
$0
是您用来运行脚本的名称。$1
、$2
等是脚本的位置参数,它们保存着运行脚本时传递的命令行参数的值。
作为戈登·戴维森说,本书作者的意思是运行./test Linux
会打印Hello Linux
。执行此操作时,./test
进入特殊参数0
,并Linux
进入第一个位置参数1
。脚本通过在第一个位置参数前加上美元符号 ( ) 来扩展该参数,就像对变量所做的那样$1
。如果您改为运行./test Hello Linux for Human Beings
,则在脚本中,$1
将扩展为Linux
、、和。$2
for
$3
Human
$4
Beings
您可以编写一个简单的脚本来尝试一下:
#!/bin/bash
echo "\$0 expands to '$0'."
echo "\$1 expands to '$1'."
echo "\$2 expands to '$2'."
echo "\$3 expands to '$3'."
(继续,直到你想要的。对于高于的位置参数9
,使用${
}
参数扩展的形式,例如,10
通过编写来扩展${10}
。在使用许多位置参数的脚本中,通常使用特殊参数@
,避免重复,但是如果你愿意,你现在可以忽略它。)
尝试将其保存在文件中并将该文件标记为可执行文件,您可以通过运行将chmod +x simple
wheresimple
替换为文件名(如果不同)来执行此操作。然后,您可以使用./simple
、./simple foo
、./simple foo bar
等命令来运行它。
您会注意到,当传递的命令行参数少于三个时,与未传递的参数相对应的位置参数将扩展为空字符串。当您尝试扩展未定义的 shell 参数时,就会发生这种情况。您还会注意到,当传递更多命令行参数时,不使用第三个之后的参数。这可能是您所期望的,因为脚本根本没有引用它们。
现在尝试运行./simple *
。 shell 会扩展*
当前目录中除以 开头的文件名之外的所有文件名.
,因此其中三个将显示为前三个位置参数(如果没有那么多,则显示更少)。您可以尝试使用其他 shell 扩展来运行它,例如./simple {5..10}
。
您可以通过将包含空格的命令行参数括在引号中来传递它们。例如,尝试./simple 'foo bar' baz
。请注意,$1
扩展到foo bar
这次,而不仅仅是foo
。
由于 shell 执行各种扩展,您传递给命令的命令行参数数量并不总是很明显。查看每个参数的简单方法是将命令替换为printf '[%s]\n'
。例如:
$ printf '[%s]\n' f*
[fonts]
[fstab]
[fuse.conf]
[fwupd]
$ printf '[%s]\n' {1,3}{a..c}
[1a]
[1b]
[1c]
[3a]
[3b]
[3c]
由于你最近才开始编写 shell 脚本,Bash 参考手册可能具有挑战性,你可能不想从头到尾阅读它。但我认为即使你认为自己是一个完全的初学者,这也是一个有价值的资源。你可能会发现shell 参数很有用,因为它从你已经知道的内容开始——shell 变量——然后转到特殊参数?
(人们通常称之为参数$?
,因为这就是你扩展它的方式)。对于 Bash 的一般学习,特别是在更入门的水平,我建议这些页面, 包括Bash指南。
答案3
您应该了解的一本好书是 William Shotts 的《Linux 命令行》,由 No Starch Press 出版,可在以下网站免费获取 PDF 版本:作者网站。
在每个 shell 脚本中,都有一组编号变量,通常称为$1
、$2
等。这些是“位置参数”,通常称为命令行参数。您可以将它们视为名为1
、2
等的变量,要获取它们的值,可以使用$1
、等。当您通过命令行$2
调用名为 的脚本时,它将获得三个参数,这些参数存储在三个变量、和 中。您无法分配给这些变量(除非作为一个组),但您可以检查和使用它们的值。例如,将打印脚本的第一个参数。my_script
./my_script a b c
$1
$2
$3
echo "$1"
$0
有点不寻常;它是您正在运行的脚本的名称。在上面的例子中,它的值为./my_script
。同样,您可以看到它的值,但不能更改它。
$?
是刚刚运行的命令的“退出状态”。如果命令成功,则其退出状态为,0
否则为一个小的正整数。您可以将$?
其与零进行比较以查看前一个命令是成功还是失败。例如,以下两个命令行将运行该grep
命令,然后<1>
因为grep
失败而回显并以状态退出1
(表示失败)。
grep blarg /etc/passwd
echo "<$?>"
退出状态有助于编写简单的脚本,例如:
#!/bin/bash
# grep_pw: grep for a pattern in /etc/passwd
grep "$1" /etc/passwd
if [[ $? = 0 ]] ;then
echo "Found it"
exit 0
else
echo "Unable to find the pattern '$1' in /etc/passwd"
exit 1
fi
将该文本放入名为的文件中grep_pw
,将其更改为可执行文件chmod 700 grep_pw
,然后调用它以查找包含字符串的./grep_pw nologin
行。/etc/passwd
nologin
当我第一次了解 shell 时,我发现以下脚本对于理解 shell 如何解析命令行以及因此将哪些命令行参数传递给脚本非常有价值。
#!/bin/bash
# echoargs: print all the arguments
counter=1
for a in "$@" ;do
echo "arg $counter=<$a>"
let counter=counter+1
done
将这些内容放入名为 的文件中echoargs
,将其更改为可执行文件chmod 700 echoargs
并按如下方式调用它:./echoargs a "b c" d
。