![我可以让 cd 成为某个函数的本地函数吗?](https://linux22.com/image/181086/%E6%88%91%E5%8F%AF%E4%BB%A5%E8%AE%A9%20cd%20%E6%88%90%E4%B8%BA%E6%9F%90%E4%B8%AA%E5%87%BD%E6%95%B0%E7%9A%84%E6%9C%AC%E5%9C%B0%E5%87%BD%E6%95%B0%E5%90%97%EF%BC%9F.png)
是否可以做一个类似的函数
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 继承所有这些属性,但没有向另一个方向传递。exit
in a subshell 仅退出子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,当你想以某种方式限制目录更改的范围时——利用该目录是有意义的堆通过使用pushd
和popd
.这是构建脚本等领域的常见技术。
假设您正在构建一堆插件。
for plugindir in plugin1 plugin2 plugin2 plugin4; do
pushd -- "$plugindir"
make
popd
done
答案4
在子 shell 中调用它。
(doStuffAt some/path/)