如何在Linux shell中以不同的工作目录运行程序?

426
在使用Linux shell时,如何以不同于当前工作目录的工作目录启动一个程序?
例如,我有一个二进制文件helloworld,它会在当前目录中创建文件hello-world.txt

此文件位于目录/a内。

目前,我在/b目录中。我想要运行../a/helloworld以获取hello-world.txt并将其放在第三个目录/c中。


6
我曾经吃过亏,发现 su 命令在运行 -c 命令前会将当前工作目录重置为指定用户的主目录。这一点对我非常有帮助。 - Patrick M
8
我发布这个问题已经有12年了,今天我自己查找了答案,因为我不记得怎么做了。 - Anton Daneyko
2
回到一个已经有“a:visited”链接和点赞的SO帖子是一回事。但当你是提问者,甚至是回答者时,情况就不同了。应该为此设立一个徽章...“健忘者”。 - Patrick M
12个回答

673

请这样调用程序:

(cd /c; /a/helloworld)
括号会导致生成子Shell。该子Shell然后将其工作目录更改为/c,然后从/a执行helloworld。程序退出后,子Shell终止,返回到父Shell的提示符处,位于您开始的目录中。
错误处理:为了避免在未更改目录的情况下运行程序(例如,拼错了/c),使helloworld的执行有条件。
(cd /c && /a/helloworld)

减少内存使用:为了避免在执行hello world时子shell浪费内存,可以通过exec调用helloworld

(cd /c && exec /a/helloworld)

[感谢Josh和Juliano提供的建议,改进了这个答案!]


3
有没有办法将参数传递给这个shell?例如$1和$2? - finiteloop
2
@segfault:子Shell可以完全访问周围的范围。 - David Schmitt
2
你可以通过使用 $*、$@ 或 "$@"(如果你想让参数遵循双引号)来传递所有参数。 - Marcel Valdez Orozco
1
如果我将这行代码添加到 /etc/rc.d/rc.local 中,它会起作用吗? - Pratik Patil
3
整个()结构的退出码将与其中最后一个命令的退出码相同。您可以通过链接直接检查或检查 $?来验证。 - David Schmitt
显示剩余2条评论

117

David Schmitt的回答类似,加上Josh的建议,但不会留下一个正在运行的shell进程:

(cd /c && exec /a/helloworld)

这种方式更类似于您通常在命令行上运行命令的方式。要了解实际差异,您需要使用每个解决方案从另一个 shell 运行 ps ef


Bash 手册内置命令 exec - Antonios Hadjigeorgalis
4
在IT领域中,exec命令用于替换当前的shell进程为指定的命令,而不会创建新的进程。 - Константин Ван

38
一个不需要子shell且内置于bash中的选项。
(pushd SOME_PATH && run_stuff; popd)

演示:

$ pwd
/home/abhijit
$ pushd /tmp # directory changed
$ pwd
/tmp
$ popd
$ pwd
/home/abhijit

2
Sahil已经提出了类似的建议。如果命令失败,则该方法无效。考虑使用pushd SOME_PATH && run_stuff && popd-- 如果run_stuff失败,则popd将不会被执行。 - Anton Daneyko
9
我认为pushd "${SOME_PATH}" && run_stuff; popd比当前的答案更好,因为pushd/popd的语义是专门为了进入某个目录然后返回到原始目录而设计的。 - Marcel Valdez Orozco
它作为别名被定义,我需要传递一个参数,它如何工作? - DrB
我认为你需要编写一个shell脚本,通过该脚本传递参数来执行一系列命令,因为你无法在别名中间传递参数。如果你需要帮助编写脚本,你应该提出一个单独的问题并引用这个问题。一旦你有了shell脚本,你可以编写一个别名来调用你的新脚本。 - Loren
1
但是...圆括号会创建一个子shell。 - tripleee
显示剩余3条评论

22
sh -c 'cd /c && ../a/helloworld'

1
在FreeBSD的jexec中使用此语句可以在指定的工作目录中执行jail内部的命令。 - Marián Černý

14

只需将最后一个"&&"替换为";",无论命令成功与否,都会切换回上一级目录:

cd SOME_PATH && run_some_command ; cd -

9

我一直认为UNIX工具应该编写成过滤器,从stdin读取输入并将输出写入stdout。如果可能的话,你可以更改你的helloworld二进制文件,将文本文件的内容写入stdout而不是特定的文件。这样你就可以使用shell在任何地方写文件。

$ cd ~/b

$ ~/a/helloworld > ~/c/helloworld.txt

6
虽然答案只是间接回答,但因为其正确性而得到了+1的评价。 - David Schmitt

5

为什么不保持简单呢

cd SOME_PATH && run_some_command && cd -

最后的 'cd' 命令会把你带回到之前的 pwd 目录。这个命令可以在所有 *nix 系统中使用。


你说得对,@mezhaka,我应该考虑到这一点 :) - Sahil
3
警告:如果run_some_command失败,cd -将不会被执行。 - starbeamrainbowlabs

4

一种方法是创建一个包装外壳脚本。

该外壳脚本将更改当前目录为 /c,然后运行 /a/helloworld。一旦外壳脚本退出,当前目录将恢复为 /b。

这里有一个 bash shell 脚本示例:

#!/bin/bash
cd /c
/a/helloworld

2

如果您希望文件总是保存到/C目录下,请在写入文件时使用绝对路径。


1

以下内容适用于我简单的需求,即设置一个别名以便附加参数,而无需将指令放在函数中;在Bash和ZSH中均可使用:

$ CWD=/your/target/dir command args

我的使用情况:我有一个名为run-service的shell脚本,它根据自己的位置解析其他文件的路径。如果我使用cd ~/Code/company/scripts && run-service dev/some-service调用它,它将期望在~/Code/company/services/dev/some-service中找到配置文件。因此,我不必总是进入目录并调用脚本,我只需这样做:
# Alias definition
alias run-service="CWD=/your/target/dir run-service"

# Calling the alias
$ run-service foo

这个方法可能过于简单,不适用于一般情况,但对我基本的使用场景有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接