重击字符串用一个替换多个字符


8

我从提要标题中将字母和数字以外的所有字符替换为破折号,以将结果用作任何文件系统的安全文件名:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

但是我想将所有重复的破折号压缩成一个 Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

我发现我可以使用两遍替换来实现:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

我以为我可以像这样通过一个步骤:

$ echo ${t//[^A-Za-z0-9]+/-}

但这不起作用。

有什么线索吗?

注意:我不想使用sed或其他工具

Answers:


8

您需要比传统的外壳通配符更强大的功能。在bash中,设置extglob选项,它使您可以通过从ksh继承的异常语法访问glob模式中的正则表达式。

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}

谢谢,在此解决方案的jw013答案下有一个评论。有关与此语法的其他shell兼容性的一些信息?我并不太担心它,只是了解更多信息shopt以及哪些外壳支持它。
neurino

@neurino shopt专用于bash。它启用的模式语法在所有ksh变体中始终可用。在zsh中,必须使用启用此语法setopt ksh_glob。POSIX没有这种功能,其通配符不如正则表达式强大。除bash / ksh / zsh之外的其他shell(在当今实际上实际上多数是指ash)都倾向于使用POSIX通配符。
吉尔(Gilles)'所以

好了,在这一点上,我更喜欢具有更高的兼容性和灵活性,而又需要更多的开销:echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'。我完全接受您的回答,因为它确实可以解决问题。
neurino

@neurino如果您想移植到其他外壳,则可以使用glenn jackman的答案。顺便说一下,请注意,该${var/PATTERN/REPLACEMENT}构造还特定于ksh / bash / zsh。
吉尔(Gilles)“所以,别再邪恶了”,

我更喜欢,sed因为我对它的语法和行为更加了解,所以我可以轻松地添加一条语句以删除开始/结尾的破折号,而无需关心\nchar。有没有sed办法比tr
neurino

7

tr 是这项工作的好工具

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}

谢谢,+ 1,我从没想起tr...但是,我试图在Bash中完成它,否则我会选择sedecho "$t" | sed -r 's/[^A-Za-z0-9]+/-/g'
neurino 2011年

投下反对票,因为它与Note: I don't want to go with sed or other tools
保罗·卡拉布罗

3

如果您想使用纯bash,则必须解决两次通过的问题。Bash字符串替换使用globs,就像在路径名扩展中一样,而不是正则表达式。在水珠的唯一特殊字符*?[],其粗糙的等价物在正则表达式是.*.[]。查看Wooledge Wikibash(1)手册页Parameter ExpansionPathname Expansion以获取更多信息。

就像评论一样,纯bash的两遍扩展可能仍然比尝试通过调用外部程序来完成相同的事情要快,因此我不必为此担心太多。


谢谢,我将检查链接。我担心的是,我必须在整个脚本中多次执行此工作,因此我唯一关心的是反复重复相同的代码,而这又一次损害了可读性。无论如何,我要提出一个礼貌的解决方案。干杯
neurino 2011年

您可以将该代码放入函数中,以避免重复代码。
jw013 2011年

这就是我正在做的事情,但是,正如您所知,bash函数无法返回字符串……或者至少是我在10分钟前才想到的:)
neurino 2011年

4
以下是一些“做”与“不做”的示例-Bash扩展globbing ..对于上述示例,应为:shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}"
Peter.O 2011年

1
@fered:谢谢,非常有趣,我会检查一下。您的链接网址有一个额外的字符,并返回404,有效的是Bash Extended Globbing
neurino
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.