Contents

合并和执行多个Linux命令

1. 概述

在本教程中,我们将看到可以有效组合和执行多个 Linux 命令的不同方式。我们将在我们的示例中使用 Bash,因此与其他 shell 可能会略有不同。

2. 为什么要组合多个命令?

在命令行中一个接一个地执行命令是 Linux 管理员的常规活动。但是,这样做,我们可能会面临以下问题:

  • 第一个命令可能需要很长时间才能完成——必须等到那时才能运行第二个命令
  • 当多个命令一个接一个运行时,我们可能会错过一个特定的命令
  • 我们可能会拼错特定的命令,这可能会导致意想不到的后果
  • 一个接一个地运行多个命令既耗时又烦人

为了防止这种情况发生并获得预期的结果,我们可以在命令行中组合并执行多个命令

3. 用“;”连接命令

; ” 不管前面的命令是否失败,操作员都会执行所有命令。

登录到服务器后,我们可以执行以下命令:

  • 切换到日志目录(使用cd
  • 列出最近的十个日志文件(使用 ls 和head
  • 打印日志文件的磁盘使用情况(使用du
  • 打印所有文件系统的磁盘使用情况(使用df

尽管如此,我们可以使用“ ; ”将上述所有任务连接在一行中。*“*运算符:

$ 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命令成功还是失败,整个命令都按我们的预期执行。

接下来,让我们看看如果我们不分组 sendSMSwriteLog命令会发生什么:

$ 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

如上面的输出所示,没有对sendSMSwriteLog命令进行分组,如果站点关闭,整个命令将按我们的预期工作。

但是,如果 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