我正在设置我的Shell环境,我希望能够在zsh中使用与bash中相同的功能/别名。这些功能之一在编辑器中打开.bashrc或.zshrc(无论哪个文件都相关),等待编辑器关闭,然后重新加载rc文件。
# a very simplified version of this function
editrc() {
local rcfile=".$(basename $SHELL)rc"
code -w ~/$rcfile
. ~/$rcfile
}
我在其他一些函数中使用了rcfile
的值,因此我将其从函数声明中拉出了。
_rc=".$(basename $SHELL)rc"
editrc() {
code -w ~/$_rc
. ~/$_rc
}
# ... other functions that use it ...
unset _rc
但是,由于我很聪明,我想在脚本结尾处取消设置_rc
,但是我仍然希望函数能够正确运行。声明函数时,有没有一种聪明的方法来评估$_rc
?
我知道我可以使用eval
并将$_rc
实例以外的所有内容放在单引号中,但这似乎很痛苦,因为函数的完整版本同时使用了单引号和双引号
_rc=".$(basename $SHELL)rc"
eval 'editrc() {
echo Here'"'"'s a thing that uses single quotes. As you can see it'"'"'s a pain.
code -w ~/'$_rc'
. ~/'$_rc'
}'
# ... other functions using `_rc`
unset _rc
我猜我可以声明我的函数,然后对eval "$(declare -f editrc | awk)"
做一些魔术。痛苦永远超过其价值,但我一直对学习新事物很感兴趣。
注意:我很乐意将其概括为一个实用的函数。
_myvar=foo
anothervar=bar
myfunc() {
echo $_myvar $anothervar
}
# redeclares myfunc with `$_myvar` expanded, but leaves `$anothervar` as-is
expandfunctionvars myfunc '$_myvar'
答案 0 :(得分:3)
在声明函数时是否有一种聪明的方法来评估$ _rc?
_rc=".$(basename "$SHELL")rc"
# while you could eval here, source lets you work with a stream
source <(
cat <<EOF
editrc() {
local _rc
# first safely trasfer context
$(declare -p _rc)
EOF
# use quoted here string to do anything inside without caring.
cat <<'EOF'
# do anything else
echo "Here's a thing that uses single quotes. As you can see it's not a pain, just choose proper quoting."
code -w "~/$_rc"
. "~/$_rc"
}
EOF
)
unset _rc
通常首先使用declare -p
将变量作为要评估的字符串进行传输。然后,在“导入”变量之后,使用带引号的此处文档执行常规脚本中的任何操作。
参考阅读:
<<EOF
是here document。请注意,在这里使用引号和不引号时,解析的区别。<(..)
是process substitution source
命令读取由进程替换创建的管道。在流程替换内部,我输出要获取的功能。在这里的第一个文档中,我输出了函数名称定义以及变量local
,以便它不会污染全局名称空间。然后,使用declare -p
将变量定义输出为正确引号的字符串,稍后再由source
派生。然后,使用带引号的文档输出该函数的其余部分,这样就不必关心引号了。
代码是特定于bash的,我对zsh一无所知并且不使用它。
您也可以使用eval来做到这一点:
eval '
editrc() {
local _rc
# first safely trasfer context
'"$(declare -p _rc)"'
# use quoted here string to do anything inside without caring.
# do anything else
echo "Here'\''s a thing that uses single quotes. As you can see it'\''s not a pain, just choose proper quoting."
code -w "~/$_rc"
. "~/$_rc"
}'
但是对我来说,使用此处引用的文档定界符可以简化编写过程。
答案 1 :(得分:0)
虽然KamilCuck正在研究他们的答案,但我设计了一个函数,该函数可以接受任何函数名和一组变量名,仅扩展这些变量,然后重新声明该函数。
expandFnVars() {
if [[ $# -lt 2 ]]; then
>&2 echo 'expandFnVars requires at least two arguments: the function name and the variable(s) to be expanded'
return 1
fi
local fn="$1"
shift
local vars=("$@")
if [[ -z "$(declare -F $fn 2> /dev/null)" ]]; then
>&2 echo $fn is not a function.
return 1
fi
foundAllVars=true
for v in $vars; do
if [[ -z "$(declare -p $v 2> /dev/null)" ]]; then
>&2 echo $v is not a declared value.
foundAllVars=false
fi
done
[[ $foundAllVars != true ]] && return 1
fn="$(declare -f $fn)"
for v in $vars; do
local val="$(eval 'echo $'$v)" # get the value of the varable represented by $v
val="${val//\"/\\\"}" # escape any double-quotes
val="${val//\\/\\\\\\}" # escape any backslashes
fn="$(echo "$fn" | sed -r 's/"?\$'$v'"?/"'"$val"'"/g')" # replace instances of "$$v" and $$v with $val
done
eval "$fn"
}
用法:
foo="foo bar"
bar='$foo'
baz=baz
fn() {
echo $bar $baz
}
expandFnVars fn bar
declare -f fn
# prints:
# fn ()
# {
# echo "$foo" $baz
# }
expandFnVars fn foo
declare -f fn
# prints:
# fn ()
# {
# echo "foo bar" $baz
# }
现在看,我发现一个缺陷。假设原始函数中的$bar
用单引号引起来。我们可能不希望替换其值。可以通过一些巧妙的正则表达式后缀来解决此问题,以计算未转义的'
的数量,但我对此很满意。