276 lines
8.7 KiB
Markdown
276 lines
8.7 KiB
Markdown
|
|
# 如何创建 Bash 脚本,带有示例代码
|
|||
|
|
|
|||
|
|
> 原文:[https://python.land/the-unix-shell/creating-bash-scripts](https://python.land/the-unix-shell/creating-bash-scripts)
|
|||
|
|
|
|||
|
|
使用 bash 脚本,您可以运行命令序列。它们防止你重复你自己,并允许你存储冗长乏味的输入命令以备后用。在本文中,您将学习如何创建 Bash 脚本。您还将学习如何读取参数、使用变量以及创建 for 循环来重复操作。
|
|||
|
|
|
|||
|
|
在开始创建脚本之前,确保您熟悉基本的 Linux 命令。
|
|||
|
|
|
|||
|
|
目录
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
* [创建一个 Bash 脚本](#Create_a_Bash_script "Create a Bash script")
|
|||
|
|
* [Bash 变量](#Bash_variables "Bash variables")
|
|||
|
|
* [Bash 脚本参数](#Bash_Script_arguments "Bash Script arguments")
|
|||
|
|
* [Bash for loop](#Bash_for_loop "Bash for loop")
|
|||
|
|
* 【Bash 脚本示例
|
|||
|
|
* [启动 Bash 脚本](#Starting_a_Bash_script "Starting a Bash script")
|
|||
|
|
* [Bash 条件编程](#Bash_conditional_programming "Bash conditional programming")
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
## 创建一个 Bash 脚本
|
|||
|
|
|
|||
|
|
命令行脚本以 shebang 开始,she bang 是两个字符`#!`的序列。在这两个字符之后,你可以命名任何解释器和一串参数,就像这样:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!interpreter [optional arguments]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
一个 shell 脚本通常基于最流行的 shell Bash,因此它可以简单地变成:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
有时你会看到这个:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/usr/bin/env bash
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
使用`/usr/bin/env`时,提供的命令会在当前`$PATH`中搜索,而不是硬编码到`/bin/bash`位置。Bash 几乎总是出现在`/bin/bash`,所以第一种方法对我们来说很好。
|
|||
|
|
|
|||
|
|
然而,您可能想要使用这个技巧来创建一个可执行的 Python 脚本。Python 可以安装在任何地方,这取决于它的安装方式。我在`/usr/bin/`、`/bin`、`/usr/local/bin`都看过,就举几个吧。如果你运行的是虚拟环境,它可以在任何地方。所以搜索`$PATH`是一个好的选择,因为这将从虚拟环境中找到 Python 版本。
|
|||
|
|
|
|||
|
|
我们稍后将创建一个实际的 shell 脚本。但是首先,我们将看看变量和 for 循环,让事情更生动一些。
|
|||
|
|
|
|||
|
|
## Bash 变量
|
|||
|
|
|
|||
|
|
就像其他语言一样,Bash 也有变量。Bash 中变量的一个奇特之处在于,您可以用美元符号访问它们,但却不用它来设置它们。为了澄清,这里有一个例子:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
myvar="Hello world"
|
|||
|
|
echo $myvar
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Bash 有许多为您自动设置的保留变量。下表列出了一些更有用的方法,但并不详尽:
|
|||
|
|
|
|||
|
|
| 可变的 | 功能 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| $0 | 当前脚本的名称。 |
|
|||
|
|
| $1 … $9 | 脚本的前 9 个参数。 |
|
|||
|
|
| $# | 传递给脚本的参数数量。 |
|
|||
|
|
| $@ | 提供给脚本的所有参数。 |
|
|||
|
|
| $用户 | 运行脚本的用户的用户名。 |
|
|||
|
|
| $主机名 | 运行脚本的机器的主机名。 |
|
|||
|
|
| 美元秒 | 自脚本启动以来的秒数。 |
|
|||
|
|
| $随机 | 每次被引用时返回一个不同的随机数。 |
|
|||
|
|
| $行号 | 返回 Bash 脚本中的当前行号。 |
|
|||
|
|
|
|||
|
|
Bash 保留变量
|
|||
|
|
|
|||
|
|
## Bash 脚本参数
|
|||
|
|
|
|||
|
|
我们现在能够创建一个 shell 脚本,但是这个脚本将在您每次运行它时做同样的事情。我们可以通过使用论点让它变得更加有趣。
|
|||
|
|
|
|||
|
|
这里有个例子,叫它`arguments.sh`:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
echo "Hello $1, from $0"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果我们用一个参数运行它,结果如下:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ ./arguments.sh Weirdo
|
|||
|
|
Hello Weirdo, from ./arguments.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Bash 所做的,是把你的整个命令分割开来,并把它分配给不太有想象力的变量`$0`、`$1`、`$2`等等。如您所见,我们命令的第一部分是脚本本身的名称。这可以派上用场,在下一节中可以看到。
|
|||
|
|
|
|||
|
|
### 创建一个安全网
|
|||
|
|
|
|||
|
|
在进入正题之前,我想教你一个重要的安全网。
|
|||
|
|
|
|||
|
|
一旦开始创建脚本,就不可避免地会出错。我们都是。脚本中一个非常常见的错误可能是灾难性的。这是未设置的变量。如果你不明白我的意思,只要继续读下去,一会儿就会明白了。
|
|||
|
|
|
|||
|
|
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these *free* articles, I hope you understand! **Support me by disabling your adblocker on my website** or, alternatively, **[buy me some coffee](https://www.buymeacoffee.com/pythonland)**. It's much appreciated and allows me to keep working on this site!
|
|||
|
|
|
|||
|
|
我们可以通过使用以下命令启动脚本来防止使用未设置的变量:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
set -u
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
仅此而已!从现在开始,当您进行替换时,您的脚本会将未设置的变量视为错误。
|
|||
|
|
|
|||
|
|
例如,此脚本将失败:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
set -u
|
|||
|
|
rm -rf /$my_directory
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
输出:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ ./test.sh
|
|||
|
|
./test.sh: 3: ./test.sh: my_directory: parameter not set
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
那是因为我从来没有设置过`$my_directory`。没有`set -u`,它不会失败,而是忽略空变量,留给我们命令`rm -rf /`。我们都知道那是什么,对吧?如果由特权用户(比如`root`)执行,这个错误将会清除我的整个文件系统,而不是我想要删除的目录。
|
|||
|
|
|
|||
|
|
养成总是以`set -u`开始脚本的习惯。
|
|||
|
|
|
|||
|
|
## Bash for loop
|
|||
|
|
|
|||
|
|
一旦掌握了 Bash for-loops,命令行(尤其是脚本)的用处就会成倍增加。尽管它们看起来很吓人,但其实并没有那么难。
|
|||
|
|
|
|||
|
|
### Bash for 循环的语法
|
|||
|
|
|
|||
|
|
循环的基本语法是:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
for VARIABLE in A LIST
|
|||
|
|
do
|
|||
|
|
command1
|
|||
|
|
command2
|
|||
|
|
commandN
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 【Bash 脚本示例
|
|||
|
|
|
|||
|
|
`A LIST`部分可以是任何东西:文件名、数字或字符串。不过,在大多数脚本中,它将是一个文件名列表,因为这是我们在 Bash 中经常使用的。现在我们知道了如何创建一个循环,让我们看看我们的第一个脚本:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
echo "You can list numbers and text like this:"
|
|||
|
|
|
|||
|
|
for n in 1 2 3 four
|
|||
|
|
do
|
|||
|
|
echo "Number $n"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
echo "Or specify a range of numbers:"
|
|||
|
|
|
|||
|
|
for n in {1..5}
|
|||
|
|
do
|
|||
|
|
echo "Number $n"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
echo "Or use the output of another command:"
|
|||
|
|
for f in $(ls)
|
|||
|
|
do
|
|||
|
|
echo $f
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在上一个 for 循环中,我使用了表达式`$(ls)`。这会执行括号中的命令并替换结果。在这种情况下,`ls`被执行,for 循环被输入了`ls`打印出的文件名。
|
|||
|
|
|
|||
|
|
## 启动 Bash 脚本
|
|||
|
|
|
|||
|
|
要启动这个脚本,我们可以做两件事。首先,我们可以运行它:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ bash loop.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
我推荐的第二种方法是使文件可执行。操作系统将知道如何执行我们的文件,因为我们的 shebang 行在顶部!通过设置文件的“执行标志”可以使文件成为可执行文件,如下所示:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ chmod +x loop.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
现在,您可以使用以下命令运行该脚本:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ ./loop.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在我的例子中,输出是:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ ./loop.sh
|
|||
|
|
You can just list a bunch of numbers and text like this:
|
|||
|
|
1
|
|||
|
|
2
|
|||
|
|
3
|
|||
|
|
four
|
|||
|
|
Or specify a range of numbers:
|
|||
|
|
1
|
|||
|
|
2
|
|||
|
|
3
|
|||
|
|
4
|
|||
|
|
5
|
|||
|
|
Or use the output of another command:
|
|||
|
|
loop.sh
|
|||
|
|
notes.txt
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
对您来说可能有所不同,这取决于运行脚本的目录中有哪些文件。
|
|||
|
|
|
|||
|
|
## Bash 条件编程
|
|||
|
|
|
|||
|
|
有时,您只想在特定条件为真时运行命令。为此,我们在 bash 中使用了`if… then… else…fi`构造。
|
|||
|
|
|
|||
|
|
### 检查 bash 脚本中的参数
|
|||
|
|
|
|||
|
|
我们可以使用条件编程来改进我们之前的例子`arguments.sh`,因为它包含了一个小问题。它期望在`$1`中有一个名字,但不检查它是否真的有名字。让我们来解决这个问题:
|
|||
|
|
|
|||
|
|
[https://crumb.sh/embed/fawGEMEUYGg](https://crumb.sh/embed/fawGEMEUYGg)
|
|||
|
|
|
|||
|
|
检查 Bash 脚本中的参数
|
|||
|
|
|
|||
|
|
如果这不起作用,下面是相同代码的静态版本:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
if test -z "$1"
|
|||
|
|
then
|
|||
|
|
echo "Usage: $0 <Your name>"
|
|||
|
|
else
|
|||
|
|
echo "Hello $1, from $0"
|
|||
|
|
fi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### bash 中的测试命令
|
|||
|
|
|
|||
|
|
用`test -z`我们可以检查变量的长度是否为零。如果是这种情况,我们打印一些友好的使用说明:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
$ ./arguments.sh
|
|||
|
|
Usage: ./arguments.sh <Your name>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
test 命令可以测试很多东西,所以它确实有助于条件编程。当你在命令行输入`man test`就可以看到完整的列表。没错,你不需要什么都用谷歌!使用手册页是成为命令行忍者的一部分!您会发现,几乎所有您可以在终端中做的事情都有一个手册页。毕竟,在过去,我们没有互联网来搜索一切…
|
|||
|
|
|
|||
|
|
下面是我们比较两个值以确定它们是否相同的另一个例子:
|
|||
|
|
|
|||
|
|
[https://crumb.sh/embed/3PAwdsNPFva](https://crumb.sh/embed/3PAwdsNPFva)
|
|||
|
|
|
|||
|
|
比较 Bash 中的两个值
|
|||
|
|
|
|||
|
|
下面是静态版本,以防不起作用:
|
|||
|
|
|
|||
|
|
```py
|
|||
|
|
#!/bin/bash
|
|||
|
|
for i in {1..10}
|
|||
|
|
do
|
|||
|
|
if test $i -eq 3
|
|||
|
|
then
|
|||
|
|
echo "I found the 3!"
|
|||
|
|
else
|
|||
|
|
echo "Not looking for the $i"
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
该循环运行 10 次迭代,每次检查`$i`是否等于 3。你能预测产量吗?
|
|||
|
|
|
|||
|
|
尽管`else`-部分是可选的,但您总是需要以`fi`结尾。
|