如何调用 shell 函数作为子进程?
How to call shell functions as a child process?
我用自制软件 (brew install nvm
) 安装了 nvm
。我想在我用 Rust 编写的 CLI 中调用它,以在我的 Rust 程序中安装和切换 node.js 版本。
问题是:nvm
是一个 shell 函数,而不是文件入口点。不幸的是,由 Rust tokio::process::Command
生成的子进程仅继承父进程的环境和 PATH
,而不是 shell 函数。
Constructs a new Command for launching the program at path program, with the following default configuration:
- No arguments to the program
- Inherit the current process's environment
- Inherit the current process's working directory
- Inherit stdin/stdout/stderr for spawn or status, but create pipes for output
https://docs.rs/tokio/0.2.0/tokio/process/struct.Command.html
当我尝试调用 tokio::process::Command::new("nvm");
时,我得到:
thread 'main' panicked at 'Command not fail: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }'
如果我 运行 使用 sudo,则没有任何变化,因为我认为 nvm
对我的子进程不存在。
如何访问 nvm
,或者更简单地说,我的 Rust 程序中的 shell 函数?
回答字面上的问题
它不太可能对您有用,但如果您的 shell 是 bash 或具有类似的扩展,您可以通过环境导出 shell 函数。
给定以下名为 example.rs
的 Rust 程序:
use std::process::Command;
use std::process::Stdio;
fn main() {
Command::new("bash")
.arg("-c")
.arg("myfunction")
.stdout(Stdio::inherit())
.status();
}
...以下 shell 程序:
myfunction() { echo "This is a function call"; }
export -f myfunction
rustc example.rs && ./example
...将作为输出发出:
This is a function call
采购 In-Process
您还可以让您的 bash 解释器获取定义函数的文件,而不需要将其导出:
use std::process::Command;
use std::process::Stdio;
fn main() {
Command::new("bash")
.arg("-c")
.arg(". \"[=13=]\" && \"$@\"")
.arg("/path/to/nvm.sh")
.arg("nvm")
.arg("install")
.arg("12")
.stdout(Stdio::inherit())
.status();
}
这适用于 nvm install
,运行 用于与调用 shell 无关的副作用(下载解释器)。它 不会 在 nvm use
的上下文中做任何特别有用的事情,因为它在 运行 中的 shell 会立即退出,因此状态会发生变化立即被扔掉。
解释为什么这个答案没用
(用于重新配置用户的交互shell)
如果 nvm
可以这样调用,那么它一开始就不需要是 shell 函数。
nvm 作为一个 shell 函数的全部意义在于让它修改调用它的 shell 的状态(修改 shell 的变量,工作目录,或其他内部状态)。您不能从任何进程修改 shell 的状态,除了 shell 本身、期间、full-stop.
你可以做的是让一个程序将有效的 shell 语法写入它的标准输出,并让 shell 其中定义了函数的 eval 该程序的输出。在不存在其他选项的此类用例中,这是一种相当常见的模式——请参阅人们 运行 eval "$(ssh-agent -s)"
的示例。
请注意,bash 确实允许您将函数导出到环境中——您可以 运行 export -f nvm
,然后是一个也是 [=57= 的副本的子进程] 将定义一个 nvm
函数——但这没有什么价值,因为 bash 的新副本有一个单独的环境;更改它不会修改父 shell 的环境,并且 如果 nvm 命令不需要修改父 shell 的环境,则不需要首先是一个函数。
我用自制软件 (brew install nvm
) 安装了 nvm
。我想在我用 Rust 编写的 CLI 中调用它,以在我的 Rust 程序中安装和切换 node.js 版本。
问题是:nvm
是一个 shell 函数,而不是文件入口点。不幸的是,由 Rust tokio::process::Command
生成的子进程仅继承父进程的环境和 PATH
,而不是 shell 函数。
Constructs a new Command for launching the program at path program, with the following default configuration:
- No arguments to the program
- Inherit the current process's environment
- Inherit the current process's working directory
- Inherit stdin/stdout/stderr for spawn or status, but create pipes for output
https://docs.rs/tokio/0.2.0/tokio/process/struct.Command.html
当我尝试调用 tokio::process::Command::new("nvm");
时,我得到:
thread 'main' panicked at 'Command not fail: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }'
如果我 运行 使用 sudo,则没有任何变化,因为我认为 nvm
对我的子进程不存在。
如何访问 nvm
,或者更简单地说,我的 Rust 程序中的 shell 函数?
回答字面上的问题
它不太可能对您有用,但如果您的 shell 是 bash 或具有类似的扩展,您可以通过环境导出 shell 函数。
给定以下名为 example.rs
的 Rust 程序:
use std::process::Command;
use std::process::Stdio;
fn main() {
Command::new("bash")
.arg("-c")
.arg("myfunction")
.stdout(Stdio::inherit())
.status();
}
...以下 shell 程序:
myfunction() { echo "This is a function call"; }
export -f myfunction
rustc example.rs && ./example
...将作为输出发出:
This is a function call
采购 In-Process
您还可以让您的 bash 解释器获取定义函数的文件,而不需要将其导出:
use std::process::Command;
use std::process::Stdio;
fn main() {
Command::new("bash")
.arg("-c")
.arg(". \"[=13=]\" && \"$@\"")
.arg("/path/to/nvm.sh")
.arg("nvm")
.arg("install")
.arg("12")
.stdout(Stdio::inherit())
.status();
}
这适用于 nvm install
,运行 用于与调用 shell 无关的副作用(下载解释器)。它 不会 在 nvm use
的上下文中做任何特别有用的事情,因为它在 运行 中的 shell 会立即退出,因此状态会发生变化立即被扔掉。
解释为什么这个答案没用
(用于重新配置用户的交互shell)
如果 nvm
可以这样调用,那么它一开始就不需要是 shell 函数。
nvm 作为一个 shell 函数的全部意义在于让它修改调用它的 shell 的状态(修改 shell 的变量,工作目录,或其他内部状态)。您不能从任何进程修改 shell 的状态,除了 shell 本身、期间、full-stop.
你可以做的是让一个程序将有效的 shell 语法写入它的标准输出,并让 shell 其中定义了函数的 eval 该程序的输出。在不存在其他选项的此类用例中,这是一种相当常见的模式——请参阅人们 运行 eval "$(ssh-agent -s)"
的示例。
请注意,bash 确实允许您将函数导出到环境中——您可以 运行 export -f nvm
,然后是一个也是 [=57= 的副本的子进程] 将定义一个 nvm
函数——但这没有什么价值,因为 bash 的新副本有一个单独的环境;更改它不会修改父 shell 的环境,并且 如果 nvm 命令不需要修改父 shell 的环境,则不需要首先是一个函数。