在参数扩展中转义特殊字符

pre*_*ers 5 zsh shell-script

file=/tmp/text/foo.txt
Run Code Online (Sandbox Code Playgroud)

现在我想/bar使用参数扩展语法在此目录结构中添加一个子文件夹。这条线的工作原理:

echo "${file%/*}/bar/${file##*/}"
Run Code Online (Sandbox Code Playgroud)

现在我想知道是否可以通过${parameter/%pat/string}语法使其更短。我想制作/pat,并将其替换为/bar/。但我不知道如何告诉系统哪个/是拍拍部分。我试图用 来逃避它\,但没有成功。我写的代码是:

echo "${f/%///bar/}"
Run Code Online (Sandbox Code Playgroud)

或者

echo "${f/%\///bar/}"
Run Code Online (Sandbox Code Playgroud)

但它们都不起作用。

Gil*_*il' 5

使用历史修改器

\n
for file in /tmp/text/foo.txt /foo.txt foo.txt mydir/; do\n  echo $file:h/bar/$file:t\ndone\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,除了系统上根目录中的文件//some/where不同于/some/where. 特别是,如果$file根本没有目录部分,它就可以工作,而天真的版本则${file%/*}没有。如果$file以斜杠结尾,bar则插入到最后一个非空路径组件之前,这可能是也可能不是您想要的。

\n

通过参数扩展形式,你可以使用参数扩展标志来选择第n个匹配,但不幸的是你不能从最后选择。因此在这种特定情况下有效,但需要确切地知道它们有多少层目录。你可以数一下:${VARIABLE/PATTERN/REPLACEMENT} Iecho ${(I:3:)file/\\///bar/}

\n
echo ${(I:${#file//[^\\/]/}:)file/\\///bar/}\n
Run Code Online (Sandbox Code Playgroud)\n

但这比拆分成多个部分更复杂,并且如果 中没有斜杠,它也不会工作得更好$file

\n

extended_glob打开后,您可以在模式中使用通配符b 标志,这使您可以匹配尾随的非斜杠字符(感谢通配符# 并用于$match引用通配符匹配的部分。

\n
setopt extended_glob\nfor file in /tmp/text/foo.txt /foo.txt foo.txt mydir/; do\n  echo ${file/%(#b)([^\\/]#)/bar/$match[1]}\ndone\n
Run Code Online (Sandbox Code Playgroud)\n

模式为(#b)([^\\/]#),匹配尾随的非斜杠字符并将匹配的部分保存在$match[1]. 使用 zsh \xe2\x89\xa55.9,您可以编写,即使关闭${(*)file/%(#b)([^\\/]#)/bar/$match[1]}它也能工作。extended_glob这并不简单,但它具有鲁棒性的优点:即使对于没有目录部分的文件名的常见情况和根目录中文件的边缘情况,结果也是正确的。mydir/bar/如果$file以斜杠结尾,它会产生,这可能是也可能不是您想要的。

\n