![用 sed 删除完全相同的匹配行](https://linux22.com/image/67681/%E7%94%A8%20sed%20%E5%88%A0%E9%99%A4%E5%AE%8C%E5%85%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E5%8C%B9%E9%85%8D%E8%A1%8C.png)
我有这个 bash 脚本:
sed -i -r '/deb http\:\/\/httpredir\.debian\.org\/debian jessie main/d' /etc/apt/sources.list
sed -i -r '/deb http\:\/\/httpredir\.debian\.org\/debian jessie\-updates main/d' /etc/apt/sources.list
sed -i -r '/deb http\:\/\/security\.debian\.org jessie\/updates main/d' /etc/apt/sources.list
echo -e 'deb http://httpredir.debian.org/debian stable main contrib non-free\ndeb-src http://httpredir.debian.org/debian stable main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb http://security.debian.org jessie/updates main contrib non-free\ndeb-src http://security.debian.org jessie/updates main contrib non-free' >> /etc/apt/sources.list
如果我按照这个顺序运行,没有问题,匹配的行将从Debian的源列表文件中删除。但如果我先回显,而不是删除行,它也会删除第 5-7 行。
将 SPACE 更改为 \s 没有帮助。
当我的 Dockerfile 更新得不够快并且 Debian 发布新的次要版本时,这可能会在将来造成麻烦。
看起来好像全局标签被应用到这个正则表达式。定义行的开始和结束对 bash 也没有帮助(尽管在 regexr 上效果很好,但这就是我想要得到的): http://regexr.com/3b910
似乎您无法使用 sed 定义 /igm 标志。 sed 仍然删除整个匹配行。
编辑:我用这种方式缩短了,但我做错了,因为它删除了文件中的所有内容:
sed /etc/apt/sources.list -i -e '\!deb http://httpredir\.debian\.org/debian jessie main$!d' -e '\!deb http://httpredir\.debian\.org/debian jessie-updates main$!d' -e '\!deb http://security\.debian\.org jessie/updates main$!d' -e '$a \ndeb http://httpredir.debian.org/debian stable main contrib non-free\ndeb-src http://httpredir.debian.org/debian stable main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb http://security.debian.org jessie/updates main contrib non-free\ndeb-src http://security.debian.org jessie/updates main contrib non-free'
这是我对问题的修改(RUN命令是Dockerfile脚本):
RUN sed -i '/^deb http\:\/\/httpredir\.debian\.org\/debian jessie main$\|^deb http\:\/\/httpredir\.debian\.org\/debian jessie\-updates main$\|^deb http\:\/\/security\.debian\.org jessie\/updates main$/d' /etc/apt/sources.list
RUN echo -e 'deb http://httpredir.debian.org/debian stable main contrib non-free\ndeb-src http://httpredir.debian.org/debian stable main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb http://security.debian.org jessie/updates main contrib non-free\ndeb-src http://security.debian.org jessie/updates main contrib non-free' >> /etc/apt/sources.list
我之所以将删除和回显过程分开,是因为也许我会将这个 Dockerfile 用于另一个新的超轻量级 Linux 发行版,这些发行版默认没有安装 sed,但可能有 echo。这样,如果我无法删除这些行,仍然会回显我的修改。
多行解决方案的问题是我想保持轻量级(只有一个 Dockerfile)并且我不想包含任何外部 .sh 脚本。
答案1
您的 sed 模式未锚定在末尾,因此它们与新行匹配。另一种解决方案是修改行而不是删除和读取。
首先让我们看看是否可以通过不那么宽来使原始脚本更具可读性:
sed -i -r '/deb http\:\/\/httpredir\.debian\.org\/debian jessie main/d' /etc/apt/sources.list
sed -i -r '/deb http\:\/\/httpredir\.debian\.org\/debian jessie\-updates main/d' /etc/apt/sources.list
sed -i -r '/deb http\:\/\/security\.debian\.org jessie\/updates main/d' /etc/apt/sources.list
cat >> /etc/apt/sources.list << EOF
deb http://httpredir.debian.org/debian stable main contrib non-free
deb-src http://httpredir.debian.org/debian stable main contrib non-free
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb http://httpredir.debian.org/debian jessie-updates main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free
deb http://security.debian.org jessie/updates main contrib non-free
deb-src http://security.debian.org jessie/updates main contrib non-free
EOF
实际上,您知道,我们没有理由不能用一个 sed 命令完成整个事情。
sed /etc/apt/sources.list -i \
-e '\!deb http://httpredir\.debian\.org/debian jessie main$!d' \
-e '\!deb http://httpredir\.debian\.org/debian jessie-updates main$!d' \
-e '\!deb http://security\.debian\.org jessie/updates main$!d' \
-e '$a \
deb http://httpredir.debian.org/debian stable main contrib non-free\
deb-src http://httpredir.debian.org/debian stable main contrib non-free\
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free\
deb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free\
deb http://httpredir.debian.org/debian jessie-updates main contrib non-free\
deb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free\
deb http://security.debian.org jessie/updates main contrib non-free\
deb-src http://security.debian.org jessie/updates main contrib non-free'
那么我们来谈谈我做了什么。首先是将 $ 添加到模式的末尾,尽管此时不需要,因为使用单个 sed 命令,您的竞争条件窗口要窄得多,甚至可能被删除,因为原始文件保留在原处,直到 sed 完成此时 sed 将其临时文件复制到原始文件上,并且这只发生一次。接下来是减少过度转义。特别是 - 和 : 在基本或扩展正则表达式中都不是特殊字符,因此它们不需要转义(这样做可能会正式导致每个 POSIX 的未定义行为,但在实践中很少这样做)。当我们这样做时,有很多斜杠需要转义,所以让我们将分隔符更改为!因为它没有在任何地方通过使用 \c 匹配运算符来使用。说到基本正则表达式和扩展正则表达式之间的差异,您没有使用任何有争议的运算符,因此您的模式被解释为哪种并不重要,所以让我们删除 -r 选项。现在,由于我们在一次 sed 调用中使用多个 sed 命令,因此我们需要 -e 选项,并且每个 sed 命令都需要它。在这种情况下,这具有额外的优点,即命令不必位于要修改的文件之前。我们用 a 命令来附加新行来结束它,并用 $ 限制它,所以我们只在最后一行执行它。我们可以通过不转义来保存六个字符。因为它与自身匹配,并且不太可能与长模式和 url 的性质匹配其他任何内容,但已经足够好了。
或者这是一个 perl 版本:
perl -pi \
-e 'next if m!deb http://httpredir\.debian\.org/debian jessie main$!;' \
-e 'next if m!deb http://httpredir\.debian\.org/debian jessie-updates main$!;' \
-e 'next if m!deb http://security\.debian\.org jessie/updates main$!;' \
-e 'END print "
deb http://httpredir.debian.org/debian stable main contrib non-free
deb-src http://httpredir.debian.org/debian stable main contrib non-free
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb http://httpredir.debian.org/debian jessie-updates main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free
deb http://security.debian.org jessie/updates main contrib non-free
deb-src http://security.debian.org jessie/updates main contrib non-free\n";}'
可以是这样的:
perl -pie 'next if m!deb http://httpredir\.debian\.org/debian jessie main$!; next if m!deb http://httpredir\.debian\.org/debian jessie-updates main$!; next if m!deb http://security\.debian\.org jessie/updates main$!; END{ print "deb http://httpredir.debian.org/debian stable main contrib non-free\ndeb-src http://httpredir.debian.org/debian stable main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free\ndeb http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb-src http://httpredir.debian.org/debian jessie-updates main contrib non-free\ndeb http://security.debian.org jessie/updates main contrib non-free\ndeb-src http://security.debian.org jessie/updates main contrib non-free\n";}'