我一直在研究我的 zsh 配置,并且对 zsh 扩展语法的几个点感到困惑。其中很多内容都是基于我在以下资源中找到的内容:
- http://zsh.sourceforge.net/Doc/Release/Expansion.html
- http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html
- https://git.madduck.net/etc/zsh.git
- https://github.com/aspiers/shell-env/
- https://stackoverflow.com/questions/9901210/bash-source0-equivalent-in-zsh
配置
我当前的 zsh 配置如下所示:
~/.config/zsh/rcsentry
~/.zshenv symlink -> ~/.config/zsh/rcsentry
~/.config/zsh/.zshenv symlink -> ~/.config/zsh/rcsentry
~/.config/zsh/.zshrc symlink -> ~/.config/zsh/rcsentry
~/.config/zsh/.zlogin symlink -> ~/.config/zsh/rcsentry
~/.config/zsh/.zprofile symlink -> ~/.config/zsh/rcsentry
我的 rcsentry 目前已被精简到最小状态,同时我试图准确理解它是如何工作的。目前它包含:
if [[ -o rcs ]]; then
rcs_fn="${(%):-%1N}"
rcs_fp="${(%):-%N}"
echo "rcs_fn: $rcs_fn"
echo "rcs_fp: $rcs_fp"
if [[ "$rcs_fn" == ".zshenv" ]]; then
echo "initializing XDG environment variables ..."
export XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}"
export XDG_CONFIG_HOME="${XDG_CONFIG:=$HOME/.config}"
export XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}"
echo "initializing ZSH environment variables ..."
export ZDOTDIR="${ZDOTDIR:=$XDG_CONFIG_HOME/zsh}"
fi
unset rcs_fn
unset rcs_fp
fi
执行新 shell 时,我得到了预期的输出:
rcs_fn: .zshenv
rcs_fp: /Users/me/.zshenv
initializing XDG environment variables ...
initializing ZSH environment variables ...
rcs_fn: .zshrc
rcs_fp: /Users/me/.config/zsh/.zshrc
rcs_fn: .zlogin
rcs_fp: /Users/me/.config/zsh/.zlogin
文档和解释
我有几件事感到困惑,但现在我试图将其限制为明确理解以下内容:
我从以下语法开始:
rcs_fn="${(%):-%1N}"
起初我根本不明白这意味着什么。即使尝试参考文档来弄清楚它也是具有挑战性的。我知道它的作用 - 但我试图弄清楚如何理解语法。所以 - 从文档开始,我发现了以下内容:
If the opening brace is directly followed by an opening parenthesis, the string
up to the matching closing parenthesis will be taken as a list of flags.
-- `man zshexpn` -> PARAMETER EXPANSION -> Parameter Expansion Flags
我相信这表明这(%)
是参数扩展语句中使用的标志列表。
% Expand all % escapes in the resulting words in the same way as in prompts
(see EXPANSION OF PROMPT SEQUENCES in zshmisc(1)). If this flag is given twice,
full prompt expansion is done on the resulting words, depending on the setting
of the PROMPT_PERCENT, PROMPT_SUBST and PROMPT_BANG options.
-- `man zshexpn` -> PARAMETER EXPANSION -> Parameter Expansion Flags
我相信这表明这(%)
实际上是一个参数扩展标志,告诉 zsh 整个语句应该像提示字符串中通常出现的那样扩展所有 % 符号。
%N The name of the script, sourced file, or shell function that zsh is
currently executing, whichever was started most recently. If there is none,
this is equivalent to the parameter $0. An integer may follow the `%'
to specify a number of trailing path components to show; zero means the
full path. A negative integer specifies leading components.
-- `man zshmisc` -> SIMPLE PROMPT ESCAPES
鉴于前面的假设,我相信这表明这%1N
是一个提示扩展,它扩展为包含 zsh 当前正在执行的代码的文件的名称。
${name:-word}
If name is set, or in the second form is non-null, then substitute its value;
otherwise substitute word. In the second form name may be omitted, in which
case word is always substituted.
-- `man zshexpn` -> PARAMETER EXPANSION
我相信这可能表明该语句的整体语法是单词替换。如果是这种情况,那么我相信名称值是空的(之前唯一的东西:
是扩展标志),因此第二部%1N
分会自动插入。不过,这对我来说确实很奇怪,就像我以某种方式完全错误地解释了语法一样。
7. Modifiers
Any modifiers, as specified by a trailing `#', `%', `/' (possibly doubled) or
by a set of modifiers of the form `:...' (see the section `Modifiers' in the
section `History Expansion'), are applied to the words of the value at this
level.
-- `man zshexpn` -> PARAMETER EXPANSION -> Parameter Expansion Flags -> Rules
After the optional word designator, you can add a sequence of one or more of the
following modifiers, each pre ceded by a `:'. These modifiers also work on the
result of filename generation and parameter expansion, except where noted.
-- `man zshexpn` -> HISTORY EXPANSION -> Modifiers
我真的不太确定如何解释这一点,或者它是否相关。看起来它可以表明:
前面(@)
是一个单词修饰符,并且后面-%1N
是文件名生成语法(对我来说没有意义)或参数扩展语法(但同样没有意义,我不知道)无法看到参数扩展部分中列出的%N,仅在提示扩展部分中)。
概括
根据我在文档中读到的内容,我认为它分解如下:
:- indicates a word expansion, with word section empty and substitution `%1N`
(%) parameter expansion flag, indicating the expression should be expanded as
if it were in prompt expansion
%1N a prompt escape, which expands to the name of the file
${..} explicit parameter expansion, required for parameter expansion flag
测试
玩弄这个,我想我已经弄清楚了语法。我更新的 rcsentry 文件包含以下内容:
if [[ -o rcs ]]; then
# The name of the file containing currently code that is currently executing.
rcs_fn="${(%):-%1N}"
# The path of the file containing currently code that is currently executing.
rcs_fp="${(%):-%N}"
# The resolved path of the file containing currently code that is currently
executing.
rcs_x1="${${(%):-%N}:A}"
# The resolved path of the parent directory of the file containing currently
# code that is currently executing.
rcs_x2="${${${(%):-%N}:A}:h}"
if [[ "$rcs_fn" == ".zshenv" ]]; then
# echo "initializing XDG environment variables ..."
export XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}"
export XDG_CONFIG_HOME="${XDG_CONFIG:=$HOME/.config}"
export XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}"
# echo "initializing ZSH environment variables ..."
export ZDOTDIR="${ZDOTDIR:=$XDG_CONFIG_HOME/zsh}"
fi
echo "rcs_fn: $rcs_fn"
echo "rcs_fp: $rcs_fp"
echo "rcs_x1: $rcs_x1"
echo "rcs_x2: $rcs_x2"
unset rcs_fn
unset rcs_fp
fi
输出如预期:
rcs_fn: .zshenv
rcs_fp: /Users/me/.zshenv
rcs_x1: /Users/me/.config/zsh/rcsentry
rcs_x2: /Users/me/.config/zsh
rcs_fn: .zshrc
rcs_fp: /Users/me/.config/zsh/.zshrc
rcs_x1: /Users/me/.config/zsh/rcsentry
rcs_x2: /Users/me/.config/zsh
rcs_fn: .zlogin
rcs_fp: /Users/me/.config/zsh/.zlogin
rcs_x1: /Users/me/.config/zsh/rcsentry
rcs_x2: /Users/me/.config/zsh
基于以上这些,我到底错了多少?
答案1
是的,您的解释是正确的(通过浏览您的长问题)。
参数扩展标志适用于参数扩展。当您希望将其应用于任意字符串时,您需要将该字符串存储在变量中,如下所示:
var=%1N
filename=${(%)var}
或者(作为一种 hack),您可以使用${var:-string}
参数扩展并将var
部分留空:${(%):-%1N}
。这是一个常见的技巧(请参阅1
2
3
4
5
6
7
8
9
10
11
12
13例如这里),尽管这会导致代码非常难以辨认。或者,您可以使用语法并使用始终设置 ( )${param+string}
的参数,例如$-
or$0
或。但这既不短也不更易读。$#
${(%)-+%1N}
在这里,您还可以使用print -P %1N