鸭子类型的嵌套问题:
function f1 { echo $1; } #with argument
function f2 { N=$((0+1)); f$N "fff"; } #with dynamic name
期望的结果:
function f2 { { echo $1; } "fff"; }
注意。抱歉,我修改了问题,真正的编码问题。
如何解决问题?
答案1
这不是 bash 所做的事情。这也不是大括号扩展。所以,我真的很想知道你在这里想做什么。
类似 Bourne 的 shell 的工作方式是通过一个可调用函数表,该表实际上仅在调用时才会展开。因此,在被解释时f1;
,函数f1
得到“被称为,”而不是像宏那样更早。
因此,你的期望的结果与您上面发布的代码关系不大,并且本质上并不等效。函数有自己的上下文和符号,这些符号除了是一个{…}
有作用域的语句集合之外还很重要。
因此,如果不改变bash
和 consorts 的工作方式,您想要的东西是不可能的 - 此时,您正在使用不同的编程语言,更类似于 TeX 而不是 shell 脚本。 (顺便说一句,TeX 作为一种语言是……原创的,但在好的方面并不是占主导地位。)
所以,正如你的问题所述,你实际上想要改变您的程序无法像以前一样工作,这涉及到调用时在函数列表中进行符号查找f1;
。这可能(并且将会)破坏一些利用重新定义函数能力的更复杂的脚本。
我觉得这不是一个好主意,但这是你的选择。你需要写一个静止的bash 脚本的解析器,即可以理解 shell 语法的解析器没有执行程序。原则上,可悲的是,
POSIX shell 语言在多个层面上挑战了编译器构造的传统智慧: shell 语言不是为静态解析而设计的,而是考虑到语法分析和扩展执行的交织。
特别是,我在这里强调:
词法分析取决于解析上下文和评估背景,
(Y. Régis-Gianas、N. Jeannerod、R. Treinen:Morbig:POSIX Shell 的静态解析器)
这意味着,一般来说,您不能静态展开f1
.您需要知道执行f1
时f2
发生了什么!
POSIX shell 的设计不是作为静态语言(没有类似一致类型系统的东西)不能对此做出任何保证。它需要一个包含 an 的条件alias f1=f3
来更改程序在运行时的内容! (还有许多其他机制可以用来解决这个问题。)换句话说,您唯一真正知道真正“含义”的时间f1
是f2
在f2
执行时的每个点,并且您需要知道 shell 的完整状态(及其环境)。
因此,要做自己想做的事,你需要成为外壳;您需要修改 bash 并添加允许您打印(保存或记录)特定内容的扩展的功能,也许可以通过挂钩。
但也许你想要的比你在问题中要求的要小得多。也许您不想修改您的 shell 脚本,但只是想在您的 shell 脚本中随时打印函数的定义?这可以通过declare
,即 来完成declare -fp f1
。
答案2
这就是你所追求的吗?
我确实对规格做了一些调整。与命令的第二个参数对应的函数作为函数 fout 加载到环境中。如果您运行脚本
. duck.sh <string> <function number>
该函数被加载到父 shell 的环境中。输出显示所选的函数,以及基于第一个参数(如果有)的输出。
文件duck.sh:
#!/bin/bash
function f1 { echo $1; } #with argument
function f2 { f1; echo "here2 "$1; }
function f3 { f1; f2; echo "The Third "$1; }
function expandInner {
while [[ $toExpand != "" ]]; do
while read -r line; do
getInner $line
done <<< $toExpand
toExpand=$(grep -w -o 'f[0-9]' <<< $inner)
done
}
function getInner {
tmpString=$(typeset -f $1)
tmpString=$(echo $tmpString | awk 'BEGIN{RS="}"; FS="{"} {print $2}')
inner=${inner/${line}/${tmpString}}
}
if [ -z "$2" ] ; then
N=$((0+1))
else
N=$2
fi
inner=$(echo $(typeset -f f$N) | awk 'BEGIN{RS="}"; FS="{"} {print $2}')
toExpand=$(grep -w -o 'f[0-9]' <<< $inner)
expandInner
echo "function fout { $inner ; echo \"fff\"; }" | tee ~/.tmp; source ~/.tmp; rm ~/.tmp
fout $1