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

par*_*oid 9 bash

我有一个源自.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
Run Code Online (Sandbox Code Playgroud)

这似乎在 zsh 中工作正常,但是当切换到 bash 时,我从 zsh 行收到错误,即使它不应该在 bash 中使用。有什么办法可以阻止它解析该行吗?

Sté*_*las 12

您可以定义一些ifbash, ifzsh,endif别名,例如:

\n
alias if{ba,z}sh=':||:<<"endif"' endif=\nif [[ -n $BASH_VERSION ]]; then\n  alias ifbash=\n  shopt -s expand_aliases\nelif [[ -n $ZSH_VERSION ]]; then\n  alias ifzsh=\nfi\n\nifbash\nf() { echo bash; }\nendif\n\nifzsh\nf() echo ${(q+):-zsh}\nendif\n
Run Code Online (Sandbox Code Playgroud)\n

:||:<<"MARKER"...MARKER是注释掉一段代码的惯用方法。这就像在 stdin 上使用 here-doc:<<"MARKER"运行:noop 内置函数,但是使用 extra 时:||,第二个:不会运行,并且 here-document 甚至没有创建,只是被解析掉。

\n

请注意,endif不得缩进,也不得在其后添加任何内容;它必须构成整条线。ifbash/ ifzshs 不能嵌套(无论如何这里都没有意义)。

\n

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

\n
\n

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

\n


Ben*_*Ben 9

Bash 仍然必须解析整个文件,即使它不会执行每一行。您通常无法将语法错误隐藏在条件后面,只能隐藏运行时错误。

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

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

  1. 仅限 zsh 的函数将进入.zshrcbash 永远不会读取的位置。同样,仅 bash 的内容也存在于.bashrc.
  2. 通用函数将有一个定义,因此您不必维护两个副本(它们将位于明确跨 shell 的文件中,因此您不太可能忘记保持它与两者兼容)。
  3. 每个 shell 都有其带有标准名称的启动文件,因此它们的配置位置更加清晰明了,并且每个 shell 都会明确声明它们也使用通用文件。
  4. 无需在 shell 字符串内维护 shell 语法(当引用比示例中的更复杂、丢失语法突出显示等时,这会变得很痛苦)。


ilk*_*chu 6

任何 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
Run Code Online (Sandbox Code Playgroud)

不过,我不确定是否source /dev/stdin适用于所有 shell 和系统。(是的,source这是内置命令的非标准名称.,但它应该在 Bash 和 zsh 中工作,并且无论如何该命令只需要存在于运行条件内部的 shell 中。)


Áng*_*gel 5

其他人已经指出了为什么 bash 从语法上解析条件的内容$shell == zsh。

在 zsh 上你可以使用

  mans() MANPAGER="less -+MFX -p ${(q+)1}" man $2
Run Code Online (Sandbox Code Playgroud)

而这不是合法的 bash 语法(函数需要 {})。

但是,在 zsh 上,您还可以对函数使用 POSIX 语法,这将使两个 shell 都满意:

elif [[ $shell == zsh ]]; then
  mans() {
    MANPAGER="less -+MFX -p ${(q+)1}" man $2
  }
fi
Run Code Online (Sandbox Code Playgroud)

此外,我通常会将它们更改[[为单一语法,这样如果在不支持的[其他 shell(例如 )上运行,文件就不会中断:dash[[

[ -n "$BASH_VERSION" ] && shell=bash
[ -n "$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
Run Code Online (Sandbox Code Playgroud)

  • @paradroid 请注意,`${(q+)var}` 在 bash 中也是无效语法。它不会在这里抱怨,因为当前版本的 bash 正在执行延迟解析,但您可能会发现未来版本的 bash 会被它阻塞。 (2认同)