我正在尝试手动从 Rails 资源中提取摘要(不要问)。我被引导到 ZMV 进行基于正则表达式的轻松查找/替换。但{32}
指定重复次数的正常语法不起作用:
$ zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9]\{32\}/}'
我尝试过其他格式。例如,这个可以工作,但太贪婪了(例如,它会变成)image-3.png
:image.png
$ zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9]##\./.}'
经过大量的 Google 搜索后,才发现这种双重哈希语法(我本以为是+
)。但我无论如何也找不到如何让它{32}
工作的方法。我试过#32#
?它似乎有效,但那是因为它将其读取为(在我看来)?32?
,这意味着它遇到了摘要或最后一个字符中包含 3 的任何内容。
如何在 zmv 中表示字符重复?
编辑:
显然,查看我试图匹配的文件名会有所帮助?明确地说:我的问题是“如何在 zmv 中表示字符重复” 不是“我如何匹配这些文件名”(我知道标准 RegEx 格式的答案)。如果有帮助,以下是我预期的前后对比:
directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css
another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz
directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js
third-directory/should-not-match-3.css
应变为:
directory/asset.css
another-directory/style.js.gz
directory/subdirectory/this-is-a-thing.js
third-directory/should-not-match-3.css
第二次编辑:
因为昨天需要这样做,所以我用很长的路走,而且(正如预期的那样)成功了。我仍然想知道将来如何避免这种情况。这是我最终使用的命令(我明确重复了我的字符匹配器 32 次):
$ zmv '(***/)(*)' '$1${2//-[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]/}'
第三次编辑:
为了记录,我在 OS X 上使用 zsh。我想象 zmv 在各个平台上是相同的,但我不能肯定。
答案1
Shell 大多不提供常用的正则表达式语法,而是提供通配符“glob”模式。基本 Shell 通配符不如正则表达式强大;例如,正则表达式.*
(任何字符序列)相当于 glob 模式*
,但正则表达式a*
(任何 's 序列a
)在普通 sh 中没有等同于 glob 模式。请参阅为什么我的正则表达式在 X 中有效,但在 Y 中无效?了解主要的不同正则表达式/模式语法的概述。
Zsh 有zsh 扩展 glob 模式它提供与正则表达式相同的表达能力,但语法不同。这些模式在zmv
和完成函数中自动启用,但在 zsh 的其他地方,它们需要使用显式启用setopt extended_glob
(将其放在您的.zshrc
- 它不是默认设置的唯一原因是与旧版本的 zsh 向后兼容)。
有一个重复 N 次的语法,但它有点隐蔽,列在通配符而不是在操作符列表下。它是c
标志,必须单独使用,后跟重复次数(或两个逗号分隔的数字以给出范围)。
zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9](#c32)/}'
答案2
我无法让它工作zmv
。一定有办法,但我记不清了。然而,zmv
并不是唯一可以做这样的事情的工具。你也可以使用rename
。
如果你使用
zsh
$ rename -n 's/-[A-Za-z0-9]{32}//' **/* another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz renamed as another-directory/style.js.gz directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css renamed as directory/asset.css directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js renamed as directory/subdirectory/this-is-a-thing.js
如果你使用
bash
$ shopt -s globstar $ rename -n 's/-[A-Za-z0-9]{32}//' **/* another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz renamed as another-directory/style.js.gz directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css renamed as directory/asset.css directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js renamed as directory/subdirectory/this-is-a-thing.js
请注意,Linux 世界中有两个rename
命令。上面的示例使用 Perl 命令,这是基于 Debian 的发行版中的默认命令。
您无法使其工作的原因zmv
是:i)它不是zmv
解释表达式,这是一个 shell 特性;因此 ii)这根本不是一个正则表达式,它是一个 glob。
当您运行问题中的命令时,zmv
设置$2
为每个文件名,然后由 shell 运行替换 ( ${2//...
)。一旦变量被 shell 扩展,它就会返回到zmv
尝试重命名操作的位置。
与 korn shell 和 bash 类似,zsh支持格式将从变量中${foo//bar}
删除 glob 的所有匹配项(与仅删除第一个匹配项的格式相反)。它的工作原理如下:bar
$foo
${foo/bar}
% foo="Xababab"
% echo ${foo//ab}
X
% echo ${foo//a*b}
X
如上所示,这些模式是 glob,而不是正则表达式。全局 a*b
表示“匹配a
,然后是 0 个或多个字符,然后是b
”。它相当于这个正则表达式:a.*b
。与正则表达式不同,globs 不支持重复(显然,zsh 的 glob 可以,参见Gilles 的回答)。x{n}
语法将不会匹配 n 次重复的 x。因此,这就是您的正则表达式失败的原因:它根本没有被解释为正则表达式!