sed:无法使用变量替换最新出现的文本,包括“-”破折号

Posted

技术标签:

【中文标题】sed:无法使用变量替换最新出现的文本,包括“-”破折号【英文标题】:sed: Can't replace latest text occurrence including "-" dashes using variables 【发布时间】:2022-01-20 19:21:29 【问题描述】:

尝试用 sed 使用变量将文本替换为另一个文本。在变量的内容包含破折号“-”并且 sed 尝试解释它之前,它工作得很好。

需要注意的是,在这种情况下,我只需要替换最近出现的原始变量 $src,这就是为什么我的 sed 命令看起来像这样:

sed -e "s:$source([^$source]*)$:$dest\1:"

“sed”对我来说是一种新事物,我总是尽可能使用“replace”或“awk”来获得结果,但在这里我试图使代码尽可能多才多艺,因此使用 sed。如果您想到其他解决方案,那也是可行的。

问题示例:

# mkdir "/home/youruser/TEST-master"
# source="TEST-master" ; dest="test-master" ; find /home/youruser/ -depth -type d -name '*[[:upper:]]*' | grep "TEST" | sed -e "s:$source([^$source]*)$:$dest\1:"
sed: -e expression #1, char 46: Invalid range end

鉴于我不知道每个变量可能包含多少个破折号,是否有任何 sed 专家知道我该如何进行这项工作?

确切的上下文:我正在为其重写一个函数以递归地小写文件和目录的开源项目 LinuxGSM。 我正在处理的 Bash 函数并在这里发表评论:https://github.com/GameServerManagers/LinuxGSM/issues/1868#issuecomment-996287057

【问题讨论】:

这不仅会在 $source 包含破折号时出错,而且还会在其他特殊字符(如括号)时出错,尤其是当有分隔符时(在您的情况下为“:”)。所以我建议用另一个 sed 转义这些字符,或者(更好的恕我直言)恢复为 awk。 括号表达式[^xyz] 匹配除xyz 之外的任何单个字符。这并不意味着除xyz 之外的任何字符串(您似乎认为它确实如此)。括号表达式始终匹配单个字符(该字符是括号之间的列表中表示的集合的成员)。 【参考方案1】:

如果我正确理解上下文,实际目标是采用在其最后一个元素中包含一些大写字符的路径,并创建一个最后一个元素小写的版本。例如,/SoMe/PaTh/FiLeNaMe 将转换为 /SoMe/PaTh/filename。如果是这种情况,不要使用字符串替换,而是使用 dirnamebasename 将其拆分为组件,最后一个大写,然后重新组装:

parentdir=$(dirname "$src")
filename=$(basename "$src")
lowername=$(echo "$latestpath" | tr '[:upper:]' '[:lower:]')
dst="$parentdir/$lowername"

(旁注:将参数引用到tr 很重要,以确保shell 不会将它们视为文件名通配符并将它们替换为匹配文件的列表。)

只要路径包含至少一个“/”但不以“/”结尾,您就可以使用 bash 替换来代替 dirnamebasename

parentdir="$src%/*"
filename="$src##*/"

只要您使用的是 bash v4.0 或更高版本,您还可以使用内置替换来进行小写:

lowername="$filename,,"

【讨论】:

你的回答是天才级别的,谢谢!当然,总有更好的方法可以在没有 sed 和避免问题的情况下做任何事情!我不知道我之前怎么没有想到你的第一个解决方案,它要简单得多!第二个换人也很棒。我会重写函数并报告。 完全重写了函数,PR 在路上。再次感谢您的提醒。在这样的时刻热爱社区! github.com/GameServerManagers/LinuxGSM/pull/3717

以上是关于sed:无法使用变量替换最新出现的文本,包括“-”破折号的主要内容,如果未能解决你的问题,请参考以下文章

使用 sed/perl/awk 替换第一次出现的匹配文本

用 sed 替换文件中最后出现的文本

如何在sed中使用变量

使用 sed 将唯一文本替换为 URL

sed命令在文件中替换、插入

如何在使用 sed 替换文本时保留新行和格式?