合并和执行多个Linux命令
1. 概述
在本教程中,我们将看到可以有效组合和执行多个 Linux 命令的不同方式。我们将在我们的示例中使用 Bash,因此与其他 shell 可能会略有不同。
2. 为什么要组合多个命令?
在命令行中一个接一个地执行命令是 Linux 管理员的常规活动。但是,这样做,我们可能会面临以下问题:
- 第一个命令可能需要很长时间才能完成——必须等到那时才能运行第二个命令
- 当多个命令一个接一个运行时,我们可能会错过一个特定的命令
- 我们可能会拼错特定的命令,这可能会导致意想不到的后果
- 一个接一个地运行多个命令既耗时又烦人
为了防止这种情况发生并获得预期的结果,我们可以在命令行中组合并执行多个命令。
3. 用“;”连接命令
“ ; ” 不管前面的命令是否失败,操作员都会执行所有命令。
登录到服务器后,我们可以执行以下命令:
尽管如此,我们可以使用“ ; ”将上述所有任务连接在一行中。*“*运算符:
$ cd logs; ls -lt | head; du -sh ; df -h
但是,我们需要记住,如果命令链中的一个命令失败,其余的命令仍然会执行。这可能会产生意想不到的输出。
4. 有条件地连接命令
上述方法并不适用于所有情况。比如根据前一条的成败执行命令。在这种情况下,我们可以使用以下方法之一。
4.1. 用“ && ”连接命令
只有当前面的命令成功时,“ &&” 或AND 运算符才会执行第二个命令。
假设我们在服务器的主目录中。我们有一个特定的文件夹archive_old,其中包含消耗大量磁盘空间的旧文件和不必要的文件。因此,我们需要删除该文件夹的内容以释放磁盘空间。
为此,我们可以一一执行以下命令:
$ cd archive_old; rm -rf *
如果文件夹名称正确,这可以正常工作。但是,假设我们不小心拼错了文件夹名称,那么所需的目录将不存在。因此,我们仍将在主目录中工作。然后,即使cd命令失败, rm 命令也会执行。结果,主目录的所有内容都将被删除。这将是致命的。
让我们看看当我们替换“ ; ”时在这种情况下会发生什么。“ && ” 运算符:
$ cd archive_oldd && rm -rf *
这样rm命令将不会被执行。这是因为“ &&”运算符只有在cd命令成功时才会执行rm命令。
4.2. 使用“||”连接命令
“ ||” 或OR 运算符仅在前面的命令返回错误时执行第二个命令。
假设我们正在创建一个新的 shell 脚本来归档日志文件。在执行新的 shell 脚本之前,我们需要使其可执行。让我们看看如何使用“||”来实现这一点。
首先,我们将检查它是否可执行,如果不是,我们将使用chmod 命令使其可执行:
$ [ -x "archive_logs.sh" ] || chmod +x archive_logs.sh
在此示例中,仅当文件archive_logs.sh不可执行时才会执行chmod命令。
5. 组命令
在 Bash 中,我们可以使用“ {} ”和“ () ”运算符来对命令进行分组。
在本节中,我们将介绍如何使用它们对命令进行分组,并通过示例了解它们之间的区别。此外,我们将讨论分组命令的日常用例。
5.1. 使用“ {} ”对命令进行分组
使用花括号“ {} ”对命令进行分组的语法是:
{ command-list; }
请注意,命令列表后面的分号(或换行符)是必需的。 让我们看一个使用“ {} ”对四个命令进行分组的示例:
$ { echo "Hi there"; pwd; uptime; date; }
Hi there
/tmp/test/blogdemo
20:27:11 up 30 days, 5:36, 1 user, load average: 0.26, 0.59, 0.77
Wed 19 Aug 2020 08:27:11 PM CEST
5.2. 使用“ () ”对命令进行分组
使用括号“ ( ) ”对命令进行分组的语法非常相似,只是命令列表后面的分号是可选的:
( command-list )
让我们使用“ ( ) ”对相同的命令进行分组:
$ ( echo "Hi there"; pwd; uptime; date )
Hi there
/tmp/test/blogdemo
20:34:05 up 30 days, 5:43, 1 user, load average: 0.97, 0.86, 0.81
Wed 19 Aug 2020 08:34:05 PM CEST
5.3. “ {} ”和“ () ”的区别
使用“ {} ”和“ () ”进行命令分组之间只有一个区别:
- { 命令;} : 命令在当前 shell 中执行
- (命令):命令将在子shell中执行
当我们更改子shell 中的变量时,这些更改在子shell 之外是不可见的。让我们看一个例子来了解这两种分组方法之间的区别。
首先,让我们使用“ {} ”对命令进行分组:
$ VAR="1"; { VAR="2"; echo "Inside group: VAR=$VAR"; }; echo "Outside: VAR=$VAR"
Inside group: VAR=2
Outside: VAR=2
如上面的输出所示,我们初始化了一个变量:VAR=”1” ,并将其值更改为组中的“ 2”。该变量也在命令组之外更新。
其次,让我们用“ ( ) ”做同样的测试:
$ VAR="1"; ( VAR="2"; echo "Inside group: VAR=$VAR" ); echo "Outside: VAR=$VAR"
Inside group: VAR=2
Outside: VAR=1
这一次,变量在子shell 中被更改。因此,更改不会影响shell。
让我们看另一个 cd命令的例子:
$ pwd; { cd /etc; pwd; }; pwd
/tmp/test/blogdemo
/etc
/etc
$ pwd; ( cd /etc; pwd ); pwd
/tmp/test/blogdemo
/etc
/tmp/test/blogdemo
在此示例中,第一个输出很简单。但是,在第二个输出中,为什么没有保留“ ( ) ”中的目录更改?
这是因为cd命令设置了环境变量*$PWD*,而pwd命令读取了变量*$PWD*。当 cd命令更新子shell中的变量*$PWD*时,更改在shell中不可见。
5.4. 我们什么时候需要对命令进行分组?
首先,我们需要在两种情况下对命令进行分组:
- 在命令列表上应用相同的重定向
- 对命令列表应用逻辑运算符
当命令被分组时,重定向可能会应用于整个命令列表:
$ ( echo "Hi there"; pwd; uptime; date ) > /tmp/output
$ cat /tmp/output
Hi there
/tmp/test/blogdemo
23:07:59 up 30 days, 8:17, 1 user, load average: 1.55, 1.95, 2.09
Wed 19 Aug 2020 11:07:59 PM CEST
我们已经知道我们可以使用*&&和||* 有条件地连接命令。使用命令组,我们可以在满足条件时执行命令列表。
比方说,我们尝试 ping 一个网站以检查它是否还活着。只有当ping命令失败时,我们才会向管理员发送 SMS 并写入日志消息。为了实现它,我们可以将sendSMS命令和writeLog 命令分组:
$ ping -c1 "some.website" 1>/dev/null 2>&1 || ( sendSMS && writeLog )
为了模拟执行,我们将使用echo命令来模拟 sendSMS 和writeLog命令,并让它们始终成功运行:
$ alias sendSMS='echo "sending SMS..(site is down!)..DONE"'
$ alias writeLog='echo "writing logs..(site is down!)..DONE"'
现在让我们用命令组做一些测试:
$ ping -c1 "site.can.never.reach" 1>/dev/null 2>&1 || ( sendSMS && writeLog )
sending SMS..(site is down!)..DONE
writing logs..(site is down!)..DONE
$ ping -c1 "www.google.com" 1>/dev/null 2>&1 || ( sendSMS && writeLog )
使用命令组,无论ping命令成功还是失败,整个命令都按我们的预期执行。
接下来,让我们看看如果我们不分组 sendSMS和writeLog命令会发生什么:
$ ping -c1 "site.can.never.reach" 1>/dev/null 2>&1 || sendSMS && writeLog
sending SMS..(site is down!)..DONE
writing logs..(site is down!)..DONE
$ ping -c1 "www.google.com" 1>/dev/null 2>&1 || sendSMS && writeLog
writing logs..(site is down!)..DONE
如上面的输出所示,没有对sendSMS和writeLog命令进行分组,如果站点关闭,整个命令将按我们的预期工作。
但是,如果 ping命令成功, 无论如何都会执行writeLog命令。这不是我们想要的。
因此,对命令进行分组可以帮助我们巧妙地解决一些问题。
6. 后台多条命令
要在后台模式下执行单个命令,我们可以使用“ & ”运算符。但是要在后台执行多个命令,我们可以使用以下两种方式之一:
- 使用“ & ”和“ && ”
- 使用“ & ”和命令组
假设我们有一个 shell 脚本,我们称之为execute_backup_db.sh,用于备份应用程序数据库。我们需要检查这个 shell 脚本需要多长时间才能完成。让我们在 shell 脚本执行前后使用date命令来获取总执行时间。 此 shell 脚本可能需要很长时间才能完成。
因此,让我们使用**“* & *”运算符在后台运行它,**并将命令输出记录在日志文件中:
$ date; ./execute_backup_db.sh; date & > execute_backup_db.log
使用“ & ”和“ ; ” 只会在后台运行最终日期命令。但是第一个 date 命令和 shell 脚本只在前台运行。相反,要有条件地在后台运行所有命令,让我们这样做:
$ date && ./execute_backup_db.sh && date & > execute_backup_db.log
用“ ; ”,让我们使用*“()”*运算符:
$ (date ; ./execute_backup_db.sh ; date) & > execute_backup_db.log