有没有办法阻止 bash 解析 zsh 代码?

有没有办法阻止 bash 解析 zsh 代码?

我有一个源自.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/ ifzshs 不能嵌套(无论如何这里都没有意义)。

顺便说一句,man似乎是在MANPAGER环境变量中单独进行引号解析(而不是调用 shell,更不用说你的了$SHELL)并且似乎不支持$'...'引号的形式,因此使用q+可能使用该形式的参数扩展标志某些字符串的引号不正确。使用qq相反,它使用单引号(并\'在引号之外转义单引号 1),就像在 bash 版本中一样。


¹ 除非rcquotes启用该选项,在这种情况下将使用''à la代替。在函数的开头rc添加emulate -L zsh,在该函数中拥有一组合理的本地选项不会有帮助,因为需要在定义函数时禁用该选项,而不是运行该选项。

答案2

Bash 仍然需要解析整个文件,即使它不会执行每一行。你通常无法隐藏句法后面的错误是有条件的,只有运行时错误。

代替你,我会继续使用单独的.zshrcand .bashrc。然后我会将常用功能放在一个第三文件(确保该文件仅使用与两者兼容的语法)并从 和 获取.zshrc.bashrc

在我看来,这是世界上最好的:

  1. 仅限 zsh 的函数将进入.zshrcbash 永远不会读取的位置。同样,仅 bash 的内容也存在于.bashrc.
  2. 通用函数将有一个定义,因此您不必维护两个副本(它们将位于明确跨 shell 的文件中,因此您不太可能忘记保持它与两者兼容)。
  3. 每个 shell 都有其带有标准名称的启动文件,因此它们的配置位置更加清晰明了,并且每个 shell 都会明确声明它们也使用通用文件。
  4. 无需在 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

相关内容