shell Bash导出函数的透明可见性

lb3vh1jj  于 2023-11-21  发布在  Shell
关注(0)|答案(1)|浏览(165)

我运行了一个实验,看看Bash的导出函数在进程链之间使用不同的shell时是否仍然可见。
令我惊讶的是,导出的函数有时仍然可见。
我正在寻找一个解释,说明如何以及为什么Bash的特定导出函数通过不同的shell在环境中维护,而它不是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

字符串
我的系统上的结果显示,除了ashdash之外,所有受试髋臼杯均保持导出的fn横向可见性:

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

pinkon5k

pinkon5k1#

环境实际上只能包含字符串,所以当你在bash中使用export -f时,它实际上只是创建了一个常规的环境变量(有一个奇怪的名字),并将其设置为函数定义的文本。例如:

$ fn(){ printf 'Hello World\n';}
$ export -f fn
$ printenv BASH_FUNC_fn%%
() {  printf 'Hello World\n'
}

字符串
在bash初始化过程中,它会扫描环境中具有这样名称的变量,并将找到的任何变量转换为函数。
由于这只是一个普通的环境变量(除了它的名字),它将在各种其他程序(包括shell)中持久化,除非程序做了一些事情将它们从环境中删除。看起来dash和ash都做到了这一点(尽管我没有ash方便测试):

$ dash -c 'printenv BASH_FUNC_fn%% || echo "not found"'
not found


你可以在其他名字奇怪的环境变量中看到同样的效果:

$ 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

相关问题