如何将shell函数传递给`git rebase --exec`

ruyhziif  于 2023-06-06  发布在  Shell
关注(0)|答案(3)|浏览(372)

在bash中,要将变量或函数传递给子进程,需要将其导出。

#!/bin/bash

f() { echo hello; }

# calling shell function `f` in child process
bash -c "f" # bash: f: command not found

# export the function
export -f f
bash -c "f" # "hello"

但是,我似乎无法使用git rebase --exec实现相同的结果

#!/bin/bash

f() { echo hello; }

# export the function
export -f f
git rebase --exec "f" # does not work
git rebase --exec "bash -c 'f'" # does not work

我知道我可以声明函数内联:git rebase --exec 'f() { echo hello; }; f'或将函数放在另一个脚本中并调用它:git rebase --exec './f.sh'
有办法让它工作吗?我错过了什么?

6ie5vjzr

6ie5vjzr1#

Bash的export -f是一个很好的黑客,而不是“标准”的东西。请参阅以下命令和输出(在Linux上):

$ foo() { echo hello; }
$ export -f foo
$ bash -c foo
hello
$ bash -c 'tr "\0" "\n" < /proc/$$/environ | grep -A 1 foo'
BASH_FUNC_foo%%=() {  echo hello
}

正如我们所看到的,bash将导出的foo()的定义放在一个名为BASH_FUNC_foo%%的env var中。
如果当前bash的子进程是另一个bash,那么子bash会很高兴地识别出这个奇怪的env var,并将其恢复为一个函数,并完成所有预期的工作。
但是如果子进程不是bash,那么行为是未定义的。子进程可以选择将这个奇怪的env变量传递给它的子进程,但是它也可以选择取消设置这个奇怪的env变量(因为它看起来很奇怪/无效或者可能是恶意的!)),所以它的子进程(比如另一个bash)不会看到它。
请注意,导出函数的env var命名格式曾经是changed,以处理shellshock问题。

de90aj5v

de90aj5v2#

尝试将函数序列化为文本:

f() { echo hello; }
git rebase --exec "$(printf "%q " bash -c "$(declare -f f); f")"
avkwfej4

avkwfej43#

如前所述,导出的函数存储在POSIX不涉及的名称空间中的环境变量中; shell既不需要也不禁止将这些名称传递给儿童。
一个简单的解决方法是将eval内容放入具有明确法律的名称的环境变量中。

#!/usr/bin/env bash
f() { echo hello; }

cmd_q="$(declare -f f); f"
cmd_q=$cmd_q git rebase --exec $'bash -c \'eval "$cmd_q"\'' "$@"

相关问题