我可以让 cd 成为某个函数的本地函数吗?

我可以让 cd 成为某个函数的本地函数吗?

是否可以做一个类似的函数

function doStuffAt {
    cd $1
    # do stuff
}

但让它调用该函数实际上并不会改变我的密码,它只是在函数的持续时间内改变它?我知道我可以保存密码并在最后将其设置回来,但我希望有一种方法可以让它在本地发生,而不必担心这一点。

答案1

是的。只需让函数在( )子 shell 中运行其命令而不是在{ }组命令中:

doStuffAt() (
        cd -- "$1" || exit # the subshell if cd failed.
        # do stuff
)

括号 ( ( )) 打开一个新的子 shell,该子 shell 将继承其父 shell 的环境。一旦运行子 shell 的命令完成,它就会退出,返回到父 shell,并且只会cd影响子 shell,因此您的 PWD 将保持不变。

请注意,子 shell 还将复制所有 shell 变量,因此您无法通过全局变量将信息从子 shell 函数传递回主脚本。

有关子 shell 的更多信息,请查看man bash

(列表)

list 在子 shell 环境中执行(请参阅下面的命令执行环境)。影响 shell 环境的变量分配和内置命令在命令完成后不再有效。返回状态是list的退出状态。

相比于:

{ 列表; }

list 只是在当前 shell 环境中执行。列表必须以换行符或分号结束。这称为组命令。返回状态是list的退出状态。请注意,与元字符 ( 和 ) 不同,{ 和 } 是保留字,并且必须出现在允许识别保留字的位置。由于它们不会导致断词,因此必须用空格或其他 shell 元字符将它们与列表分隔开。

答案2

这取决于。

您可以将该函数放入子外壳。 (也可以看看括号真的将命令放入子 shell 中吗?) 子 shell 中发生的事情保留在子 shell 中。函数对变量、当前目录、重定向、陷阱等所做的更改不会影响调用代码。子 shell 从其父 shell 继承所有这些属性,但没有向另一个方向传递。exitin a subshel​​l 仅退出子shell,而不退出调用进程。您可以通过将一段代码括在括号中将其放入子 shell 中(换行符甚至括号前后的空格都是可选的):

(
  set -e # to exit the subshell as soon as an error happens
  cd -- "$1"
  do stuff # in $1
)
do more stuff # in whatever directory was current before the '('

如果要在子 shell 中运行整个函数,可以使用圆括号而不是大括号来包裹函数的代码。

doStuffAt () (
    set -e
    cd -- "$1"
    # do stuff
)

使用 Korn 风格的函数定义语法,您需要:

function doStuffAt { (
    set -e
    cd -- "$1"
    # do stuff
) }

子 shell 的缺点是没有任何东西可以逃脱它。如果您需要更改当前目录,然后更新一些变量,则无法使用子 shell 来执行此操作。从子 shell 中检索信息只有两种简单的方法。与任何其他命令一样,子 shell 也有退出状态,但这是 0 到 255 之间的整数,因此它不会传达太多信息。您可以使用命令替换产生一些输出:命令替换是一个子 shell,其标准输出(减去尾随换行符)被收集到一个字符串中。这可以让您输出一个字符串。

data=$(
  set -e
  cd -- "$1"
  do stuff # in $1
)
# Now you're still in the original directory, and you have some data in $data

您可以将当前目录保存到变量中,并在以后恢复。

set -e
old_cwd="$PWD"
cd -- "$1"
cd "$old_cwd"

但是,这种方法不太可靠。如果代码在两个cd命令之间退出,它将位于错误的目录中。如果在此期间移动了旧目录,第二个命令cd将不会返回到正确的位置。有可能您处于一个您无权更改的目录中(因为脚本的权限低于其调用者),在这种情况下,第二个命令cd将失败。因此,除非您处于受控环境中,否则您不应该这样做(例如,进入cd和退出由脚本创建的临时目录)。

如果您需要临时更改目录并以某种方式影响 shell 环境(例如设置变量),则需要仔细地将脚本分为影响 shell 环境的部分和更改当前目录的部分。 shell继承了早期unix系统的限制,无法返回到先前的目录。现代 UNIX 系统可以(您可以“保存”当前目录的文件描述符,并fchdir()在异常处理程序中返回它),但没有针对此功能的 shell 接口。

答案3

当“临时”进入一个目录来做一些工作时——IOW,当你想以某种方式限制目录更改的范围时——利用该目录是有意义的通过使用pushdpopd.这是构建脚本等领域的常见技术。

假设您正在构建一堆插件。

for plugindir in plugin1 plugin2 plugin2 plugin4; do
  pushd -- "$plugindir"
  make
  popd
done

答案4

在子 shell 中调用它。

(doStuffAt some/path/)

相关内容