Léa*_*ris 7 bash shell function environment-variables
我进行了一项实验,看看在进程链之间使用不同的 shell 时,Bash 的导出函数是否仍然可见。
令我惊讶的是,导出的函数有时仍然可见。
我正在寻找关于如何以及为什么通过不同的 shell 在环境中维护 Bash 的特定导出函数的解释,而它不是 POSIX 标准环境变量。
不知怎的,我有一个线索,导出的函数声明作为字符串存储在(标准?)环境变量中。但它如何作为环境变量对不兼容的 shell 不可见,以及如何在环境中维护它?
这是我的实验代码:
#!/usr/bin/env bash
LANG=C
# Declare function and export it
fn(){ printf 'Hello World\n';}
export -f fn
# Standard usage, call exported function within a child bash
bash -c fn
# Available shells for tests
available_shells=()
for sh in ash bash dash ksh tcsh zsh
do
if shell_path=$(command -v "$sh" 2> /dev/null)
then
available_shells+=("$shell_path")
real_shell_path=$(realpath "$shell_path")
shell_version=$(
read -r pkg < <(apt-file search "${real_shell_path#/usr}" | awk -F: '{print $1}')
dpkg -s "$pkg" | awk '/Version:/{print $2}'
)
printf 'Found: %s\tversion: %s\n' "$shell_path" "$shell_version"
fi
done
# Test transversal visibility of exported fn through different shells
for sh in "${available_shells[@]}"; do
printf "Testing %s -c 'bash -c fn':\\n" "$sh"
"$sh" -c 'bash -c fn'
printf \\n
done
Run Code Online (Sandbox Code Playgroud)
我的系统上的结果表明,除了和 之外fn
,所有测试的 shell 都保持了导出的横向可见性:ash
dash
Hello World
Found: /usr/bin/ash version: 0.5.12-2ubuntu1
Found: /usr/bin/bash version: 5.2.15-2ubuntu1
Found: /usr/bin/dash version: 0.5.12-2ubuntu1
Found: /usr/bin/ksh version: 1.0.4-3
Found: /usr/bin/tcsh version: 6.24.07-1
Found: /usr/bin/zsh version: 5.9-4
Testing /usr/bin/ash -c 'bash -c fn':
bash: line 1: fn: command not found
Testing /usr/bin/bash -c 'bash -c fn':
Hello World
Testing /usr/bin/dash -c 'bash -c fn':
bash: line 1: fn: command not found
Testing /usr/bin/ksh -c 'bash -c fn':
Hello World
Testing /usr/bin/tcsh -c 'bash -c fn':
Hello World
Testing /usr/bin/zsh -c 'bash -c fn':
Hello World
Run Code Online (Sandbox Code Playgroud)
环境实际上只能包含字符串,因此当您export -f
在 bash 中使用时,它实际上只是创建一个常规环境变量(具有奇怪的名称)并将其设置为函数定义的文本。例如:
$ fn(){ printf 'Hello World\n';}
$ export -f fn
$ printenv BASH_FUNC_fn%%
() { printf 'Hello World\n'
}
Run Code Online (Sandbox Code Playgroud)
在 bash 初始化期间,它会扫描环境中是否存在具有此类名称的变量,并将找到的任何变量转换为函数。
由于这只是一个常规环境变量(与其名称不同),因此它将通过各种其他程序(包括 shell)持续存在,除非程序执行某些操作将它们从环境中删除。看来 dash 和 ash 都可以做到这一点(尽管我没有方便测试的 ash):
$ dash -c 'printenv BASH_FUNC_fn%% || echo "not found"'
not found
Run Code Online (Sandbox Code Playgroud)
您可以通过其他名称奇怪的环境变量看到相同的效果:
$ env test%="still here" bash -c 'printenv test% || echo "not found"'
still here
$ env test%="still here" ksh -c 'printenv test% || echo "not found"'
still here
$ env test%="still here" zsh -c 'printenv test% || echo "not found"'
still here
$ env test%="still here" dash -c 'printenv test% || echo "not found"'
not found
Run Code Online (Sandbox Code Playgroud)