Contents

运行具有不同工作目录的脚本

1. 概述

在 Linux 中编写 shell 脚本时,我们可能会遇到在编写 shell 脚本的过程中必须切换目录并执行其他脚本或程序的情况。

在本教程中,我们将了解如何在 shell 脚本中从不同目录运行脚本或程序。为了正确满足依赖关系,我们需要跟踪上下文的当前目录。

我们将在以下部分检查如何处理此问题。

2. 问题

假设我们有一些脚本放置在不同的目录中:

$ tree .
.
├── init
│   ├── init.conf
│   └── init.sh
├── run.sh
└── start.sh
1 directory, 4 files

在这里,我们有 start.sh,它调用 run.sh和 init.sh

$ cat init/init.conf 
NAME=Blogdemo
$
$ cat init/init.sh 
#!/bin/bash
. init.conf
echo $NAME
$
$ cat run.sh 
#!/bin/bash
echo "Running script"

正如我们所见,  init.sh 脚本保存在 init目录中。鉴于这种情况,我们需要正确更改start.sh中的目录, 以便其他脚本调用正常工作。

我们已经讨论过在 shell 脚本 中更改目录,但在本文中,我们将重点关注在不同调用之间维护原始上下文。

让我们看看执行此操作的不同方法。

3. 使用变量

第一个也是最简单的解决方案是将当前目录保存在一个变量中并切换到新目录。执行脚本后,我们可以使用变量将目录恢复到旧目录:

$ cat start.sh 
#!/bin/bash
CUR_DIR=$(pwd)
cd init
./init.sh
cd $CUR_DIR
echo "Starting script"
./run.sh
$
$ ./start.sh 
Blogdemo
Starting script
Running script

在上面的脚本中,我们使用pwd 命令捕获了当前目录并将其存储在变量 CUR_DIR中。执行 init.sh后,我们切换回旧目录,该目录保存在CUR_DIR中。

从输出中,我们可以看到它已经从init.sh中正确打印了名称,并且还从 start.sh和 run.sh脚本中打印了echo语句。

4. 不使用变量

在上面的解决方案中,我们使用了一个变量来存储当前目录。但是如果我们使用‘cd’ 命令转到上一个目录,我们可以在不创建中间变量的情况下获得相同的结果:

$ cat start.sh 
#!/bin/bash
cd init
./init.sh
cd -
echo "Starting script"
./run.sh
$
$ ./start.sh 
Blogdemo
/home/bluelake/Documents/shell/chdir
Starting script
Running script

在这里,调用“cd -”命令会将目录更改为旧目录。即使我们更改 init.sh 中的目录,这也有效,因为init.sh作为子进程运行。

这样做的一个问题是,当执行*“cd ”*命令时,它会将新目录回显到标准输出。如果我们需要抑制它,有两种方法可以做到这一点

一种方法是将输出重定向到空设备

cd - > /dev/null

另一个是使用Bash 内部变量 $OLDPWD

cd $OLDPWD

即使我们在没有创建变量的情况下获得了结果,但取决于我们是否需要在代码中进一步使用该变量来验证这是否真的为我们节省了任何东西。

5. 在子shell中运行

另一种解决方案是从子shell 运行 init.sh 脚本。让我们看看如何在start.sh中做到这一点:

$ cat start.sh 
#!/bin/bash
(cd init; ./init.sh)
echo "Starting script"
./run.sh
$
$ ./start.sh 
Blogdemo
Starting script
Running script

当我们需要在子 shell 中调用命令时,我们将它们放在括号中。因此,我们可以看到在更改目录后,在子 shell 中调用了init.sh脚本。这样,它单独运行而不影响当前的 shell

在这里,我们假设init目录始终存在。但是在某些情况下,我们不确定目录。对于这些情况,我们可以使用逻辑 AND 运算符来防止调用init.sh脚本:

(cd init && ./init.sh)

这样,我们确保 init 目录在执行脚本之前存在。

6. 使用pushd命令

让我们看看我们如何使用 pushd  来解决这个问题:

$ cat start.sh 
#!/bin/bash
pushd init
./init.sh
popd
echo "Starting script"
./run.sh
$
$ ./start.sh 
~/Documents/shell/chdir/init ~/Documents/shell/chdir
Blogdemo
~/Documents/shell/chdir
Starting script
Running script

在上面的脚本中,我们使用了 pushd 和popd 命令在目录之间切换。我们知道,  ** pushd 保存当前目录并切换到作为参数给出的新目录。之后,我们可以使用popd来恢复推送的目录**。

从结果中,我们可以看到一切正常。

为了抑制 pushd和 popd命令的输出,我们可以将它们重定向到*/dev/null*设备:

pushd init > /dev/null
./init.sh
popd > /dev/null

7. 使用sh命令

最后,我们可以使用sh 命令在单独的进程中运行它,这样它就不会影响当前的 shell:

$ cat start.sh 
#!/bin/bash
sh -c 'cd init && ./init.sh'
echo "Starting script"
./run.sh
$
$ ./start.sh 
Blogdemo
Starting script
Running script

如我们所见,我们已将命令作为参数传递给sh命令。从上面的结果,我们可以看出它已经被正确执行了。但是,这类似于我们使用 子shell 运行init.sh 脚本的早期方法。