我有一个源自.bashrc
并.zshrc
包含一些函数的文件。以前它是两个单独的文件(对于每个 shell),但我现在使用单个文件,因为这些文件几乎相同,并且我更容易管理单个函数文件。
有一个函数对于两个 shell 来说是不同的,所以我想我可以使用一个条件语句,以便使用正确的语句:
[[ $BASH_VERSION ]] && shell=bash
[[ $ZSH_VERSION ]] && shell=zsh
# Search man page for string
if [[ $shell == bash ]]; then
mans() {
local q="\'"
local q_pattern="'${1//$q/$q\\$q$q}'"
local MANPAGER="less -+MFX -p $q_pattern"
man "$2"
}
elif [[ $shell == zsh ]]; then
mans() MANPAGER="less -+MFX -p ${(q+)1}" man $2
fi
这似乎在 zsh 中工作正常,但是当切换到 bash 时,我从 zsh 行收到错误,即使它不应该在 bash 中使用。有什么办法可以阻止它解析该行吗?
答案1
您可以定义一些ifbash
, ifzsh
,endif
别名,例如:
alias if{ba,z}sh=':||:<<"endif"' endif=
if [[ -n $BASH_VERSION ]]; then
alias ifbash=
shopt -s expand_aliases
elif [[ -n $ZSH_VERSION ]]; then
alias ifzsh=
fi
ifbash
f() { echo bash; }
endif
ifzsh
f() echo ${(q+):-zsh}
endif
:||:<<"MARKER"
...MARKER
是注释掉一段代码的惯用方法。这就像在 stdin 上使用 here-doc:<<"MARKER"
运行:
noop 内置函数,但是使用 extra 时:||
,第二个:
不会运行,并且 here-document 甚至没有创建,只是被解析掉。
请注意,endif
不得缩进,也不得在其后添加任何内容;它必须构成整条线。ifbash
/ ifzsh
s 不能嵌套(无论如何这里都没有意义)。
顺便说一句,man
似乎是在MANPAGER
环境变量中单独进行引号解析(而不是调用 shell,更不用说你的了$SHELL
)并且似乎不支持$'...'
引号的形式,因此使用q+
可能使用该形式的参数扩展标志某些字符串的引号不正确。使用qq
相反,它使用单引号(并\'
在引号之外转义单引号 1),就像在 bash 版本中一样。
¹ 除非rcquotes
启用该选项,在这种情况下将使用''
à la代替。在函数的开头rc
添加emulate -L zsh
,在该函数中拥有一组合理的本地选项不会有帮助,因为需要在定义函数时禁用该选项,而不是运行该选项。
答案2
Bash 仍然需要解析整个文件,即使它不会执行每一行。你通常无法隐藏句法后面的错误是有条件的,只有运行时错误。
代替你,我会继续使用单独的.zshrc
and .bashrc
。然后我会将常用功能放在一个第三文件(确保该文件仅使用与两者兼容的语法)并从 和 获取.zshrc
它.bashrc
。
在我看来,这是世界上最好的:
- 仅限 zsh 的函数将进入
.zshrc
bash 永远不会读取的位置。同样,仅 bash 的内容也存在于.bashrc
. - 通用函数将有一个定义,因此您不必维护两个副本(它们将位于明确跨 shell 的文件中,因此您不太可能忘记保持它与两者兼容)。
- 每个 shell 都有其带有标准名称的启动文件,因此它们的配置位置更加清晰明了,并且每个 shell 都会明确声明它们也使用通用文件。
- 无需在 shell 字符串内维护 shell 语法(当引用比示例中的更复杂、丢失语法突出显示等时,这会变得很痛苦)。
答案3
任何 shell 都必须解析条件中的代码以找到条件结束的部分,即使没有其他原因。
除了将特定于 shell 的代码放入另一个文件或 to 的字符串中之外eval
,您还可以使用here-doc 来source
直接隐藏它和here-doc。与 using 几乎相同eval
,但 here-doc 的优点是内部代码可以包含单引号和双引号,而不会出现任何烦人的转义问题。
例如,此脚本表示hello, 'bar', I'm zsh
如果使用 zsh 运行,并在 Bash 中抛出错误:
if [[ -n $ZSH_VERSION ]]; then
source /dev/stdin <<'EOF'
foo() {
echo "hello, '$1', I'm zsh"
}
EOF
fi
foo bar
不过,我不确定是否source /dev/stdin
适用于所有 shell 和系统。 (是的,source
这是内置命令的非标准名称.
,但它应该在 Bash 和 zsh 中工作,并且无论如何该命令只需要存在于运行条件内部的 shell 中。)
答案4
方法由 @ilkkachu 建议工作:
# Search man page for string
if [[ $shell == bash ]]; then
mans() {
local q="\'"
local q_pattern="'${1//$q/$q\\$q$q}'"
local MANPAGER="less -+MFX -p $q_pattern"
man "$2"
}
elif [[ $shell == zsh ]]; then
mansfunc='mans() MANPAGER="less -+MFX -p ${(q+)1}" man $2'
eval "$mansfunc"
fi