Sed 命令删除“\”导致“*** multiple target patterns. Stop.”错误
Sed command to delete "\" which causes "*** multiple target patterns. Stop." error
在文件中,我有这样的行 -
a.lo a.o: abc/util.c \
/usr/lib/def.h
b.lo b.o: hash/imp.h \
/usr/lib/toy.c \
c.lo c.o: high/scan.c \
high/scan_f.c
在这里您可以在第 4 行 (/usr/lib/toy.c ) 的末尾看到一个额外的 \(反斜杠)。如何使用 sed 命令删除此 /(反斜杠)?因此,我得到“*** 多个目标模式。停止。”错误。
P.S。 - 我在我的文件中的多个地方有这个额外的 \(反斜杠)。因此使用 sed 按行号删除它是不可行的。需要一些可以检查 .lo .o 并检查之前的行的东西,如果它找到 \(反斜杠)将其删除。
我同意@G.M。一般情况下,但这会起作用。
sed 在以“\”开头的行上的尾随“\”(如果存在)之前捕获文本,并仅在这些行上打印该文本。当然也打印所有其他文本
sed -e 's/\(.* \)\$//' input_file
也许不是最简单的,但应该可以:
sed -nE '${s/\$//;p;};N;s/\([^\]*:)//;P;D' input_file
主要思想是在模式space(一个sed内部文本缓冲区)中连接输入行,这样它总是包含2个连续的行,由一个换行符。然后我们只删除 :
之前的最后一个 \
,如果有的话,打印 2 行中的第一行并将其从模式 space 中删除,然后再继续下一行。
sed
命令由半列 (;
) 分隔,并用大括号 ({...}
) 分组。它们前面可选地有行规范,例如 $
代表输入的最后一行。因此,在我们的例子中,${s/\$//;p;}
仅适用于最后一行,而其余 (N;s/\([^\]*:)//;P;D
) 适用于所有行。
-n
选项抑制默认输出。我们需要它来使用 p
(打印)命令自己控制输出。
-E
选项允许使用扩展的正则表达式。
让我们先解释一下棘手的部分:N;s/\([^\]*:)//;P;D
。它是 4 个命令的列表,每行输入 运行,因为在命令之前没有行规范。
当 sed
开始处理输入时,模式 space 已经包含第一行(在您的示例中为 a.lo a.o: abc/util.c \
)。这就是 sed
的工作方式:默认情况下,它将当前行置于模式 space 中,应用命令并从下一行重新开始。
N
将下一个输入行 (/usr/lib/def.h
) 附加到模式 space 并使用换行符作为分隔符。模式 space 现在包含:
a.lo a.o: abc/util.c \
/usr/lib/def.h
N
也会增加当前行号,变成 2。
s/\([^\]*:)//
删除模式 space 中第一个 :
之前的最后一个 \
,如果有的话。在我们的示例中,唯一的 \
是 在 之后第一个 :
。模式 space 未修改。
P
打印模式的第一部分 space,直到第一个换行符。在我们的示例中,打印的是:
a.lo a.o: abc/util.c \
D
删除模式 space 的第一部分,直到第一个换行符(刚刚打印的内容)。模式 space 包含:
/usr/lib/def.h
D
也开始一个新的循环,但与正常的 sed
处理不同,它不读取下一行并保留模式 space 和当前行号未修改。所以当重新启动模式时 space 包含行号 2 而当前行号仍然是 2.
通过归纳,我们看到,每次 sed
重新开始执行命令列表时,模式 space 都会正常包含当前行。在处理示例的第 4 行时,它包含:
/usr/lib/toy.c \
在N
之后包含:
/usr/lib/toy.c \
c.lo c.o: high/scan.c \
然后,替换命令 (s/\([^\]*:)//
) 匹配并删除第一个 \
:
/usr/lib/toy.c
c.lo c.o: high/scan.c \
因此:
/usr/lib/toy.c
打印并从模式中删除的 space。正是你想要的。
最后一行需要特殊处理。当我们开始处理它时,模式 space 包含:
high/scan_f.c
如果我们不做任何特殊的事情 N
不改变它(没有下一行要连接)并终止处理。最后一行从不打印。
这就是为什么需要另一个命令列表的原因,就在最后一行:${s/\$//;p;}
。它仅适用于最后一行,因为它前面有行规范($
表示最后一行)。列表中的第一个命令(替代 s/\$//
)删除尾随 \
,如果有的话。第二个 (p
) 打印模式 space.
注意:如果您知道最后一行不以反斜杠结尾,您可以稍微简化一下:
sed -nE '$p;N;s/\([^\]*:)//;P;D' input_file
关于如何识别应从中删除尾随反斜杠的行的问题有点不清楚,但由于输入看起来像一组 makefile 格式的先决条件列表,其中一些行已被删除,我以 objective 为删除列表中最后一个(剩余)先决条件之后出现的反斜杠。这需要向前看下一行,因此在向前看时利用 sed
的 hold space 来存储数据会很有帮助下一行找出如何处理它。
对于该问题,这将是一个非常可靠的解决方案:
sed -nE 's/\s*(\){0,1}$/ \/; :a; /:/ { x; s/\s*\$//; p; d; }; H; $ { s/.*/:/; b a }' input
这会在保留 space 中构建每个先决条件列表,并嵌入反斜杠和换行符,然后在下一个目标列表或输入末尾到达时将其转储。
详情:
-n
选项关闭在每行后自动打印模式 space
-E
选项打开扩展正则表达式
sed
表达式包含几个子表达式,由分号连接:
s/\s*(\){0,1}$/ \/
:确保模式 space 中的当前行以 space 和反斜杠结尾,而不向已经有一个 [=70= 的行添加第二个反斜杠]
:a
: 指向脚本中的标签 'a'
/:/ { x; s/\s*\$//; p; d; }
:在包含冒号的行上,交换模式并按住 spaces,从模式 space 的(新内容)中删除结尾的反斜杠,打印结果,然后开始下一个循环
H
:(如果控制到达这一点)将换行符和模式 space 的内容附加到保留 space
$ { s/.*/:/; b a }
:在输入触发器的最后一行通过在模式 space 中放置一个冒号并跳转到标签 'a'[=70 来转储保留 space =]
- [表达式结束]:将下一行读入模式 space 并重新开始
或者,它会更准确地遵循您的要求,并避免引入前导空行,这样做:
sed -n ':a; /\$/! { p; d; }; h; :b; $ { x; s/\//; p; }; n; /:/ { x; s/\$//; p; x; b a; }; H; /\$/ b b; s/.*//; x; p' input
在最终打印之前,它也在货舱 space 中组装零件,但它以不同的方式进行:
- 它从检查模式 space 中的行是否以反斜杠结尾开始(在标签
a
处)。如果不是 (/\$/!
),则打印模式 space 并开始下一个循环。
- 否则,它将保留 space 的当前内容替换为模式 space 的内容(必须已经以反斜杠结尾),然后
- (at label
b
) 如果当前行是最后一行,则它检索 hold space 的内容,去除尾随的换行符,并打印结果 ($ { x; s/\//; p; }
).无论哪种方式,
- 它尝试读取下一个输入行,如果没有(
n
)则终止。
- 如果这导致模式 space 中包含一个冒号,则打印保留 space 的内容,减去尾随反斜杠,并将控制发送回标签
a
将包含冒号的行作为新的第一行处理 (/:/ { x; s/\$//; p; x; b a; }
)。
- 否则,换行符和模式 space 的内容将附加到保留 space (
H
).
- 如果模式 space 以反斜杠结尾,则控制分支返回标签
b
以考虑读取另一行 (/\$/ b b
)。
- 否则,保留 space 被打印并清除 (
s/.*//; x; p
),并且
- 如果还有更多行,则读取下一行并开始新的循环。
这对输入的性质做出了更少的假设,但有点复杂。
在文件中,我有这样的行 -
a.lo a.o: abc/util.c \
/usr/lib/def.h
b.lo b.o: hash/imp.h \
/usr/lib/toy.c \
c.lo c.o: high/scan.c \
high/scan_f.c
在这里您可以在第 4 行 (/usr/lib/toy.c ) 的末尾看到一个额外的 \(反斜杠)。如何使用 sed 命令删除此 /(反斜杠)?因此,我得到“*** 多个目标模式。停止。”错误。
P.S。 - 我在我的文件中的多个地方有这个额外的 \(反斜杠)。因此使用 sed 按行号删除它是不可行的。需要一些可以检查 .lo .o 并检查之前的行的东西,如果它找到 \(反斜杠)将其删除。
我同意@G.M。一般情况下,但这会起作用。
sed 在以“\”开头的行上的尾随“\”(如果存在)之前捕获文本,并仅在这些行上打印该文本。当然也打印所有其他文本
sed -e 's/\(.* \)\$//' input_file
也许不是最简单的,但应该可以:
sed -nE '${s/\$//;p;};N;s/\([^\]*:)//;P;D' input_file
主要思想是在模式space(一个sed内部文本缓冲区)中连接输入行,这样它总是包含2个连续的行,由一个换行符。然后我们只删除 :
之前的最后一个 \
,如果有的话,打印 2 行中的第一行并将其从模式 space 中删除,然后再继续下一行。
sed
命令由半列 (;
) 分隔,并用大括号 ({...}
) 分组。它们前面可选地有行规范,例如 $
代表输入的最后一行。因此,在我们的例子中,${s/\$//;p;}
仅适用于最后一行,而其余 (N;s/\([^\]*:)//;P;D
) 适用于所有行。
-n
选项抑制默认输出。我们需要它来使用p
(打印)命令自己控制输出。-E
选项允许使用扩展的正则表达式。
让我们先解释一下棘手的部分:N;s/\([^\]*:)//;P;D
。它是 4 个命令的列表,每行输入 运行,因为在命令之前没有行规范。
当
sed
开始处理输入时,模式 space 已经包含第一行(在您的示例中为a.lo a.o: abc/util.c \
)。这就是sed
的工作方式:默认情况下,它将当前行置于模式 space 中,应用命令并从下一行重新开始。N
将下一个输入行 (/usr/lib/def.h
) 附加到模式 space 并使用换行符作为分隔符。模式 space 现在包含:a.lo a.o: abc/util.c \ /usr/lib/def.h
N
也会增加当前行号,变成 2。s/\([^\]*:)//
删除模式 space 中第一个:
之前的最后一个\
,如果有的话。在我们的示例中,唯一的\
是 在 之后第一个:
。模式 space 未修改。P
打印模式的第一部分 space,直到第一个换行符。在我们的示例中,打印的是:a.lo a.o: abc/util.c \
D
删除模式 space 的第一部分,直到第一个换行符(刚刚打印的内容)。模式 space 包含:/usr/lib/def.h
D
也开始一个新的循环,但与正常的sed
处理不同,它不读取下一行并保留模式 space 和当前行号未修改。所以当重新启动模式时 space 包含行号 2 而当前行号仍然是 2.
通过归纳,我们看到,每次 sed
重新开始执行命令列表时,模式 space 都会正常包含当前行。在处理示例的第 4 行时,它包含:
/usr/lib/toy.c \
在N
之后包含:
/usr/lib/toy.c \
c.lo c.o: high/scan.c \
然后,替换命令 (s/\([^\]*:)//
) 匹配并删除第一个 \
:
/usr/lib/toy.c
c.lo c.o: high/scan.c \
因此:
/usr/lib/toy.c
打印并从模式中删除的 space。正是你想要的。
最后一行需要特殊处理。当我们开始处理它时,模式 space 包含:
high/scan_f.c
如果我们不做任何特殊的事情 N
不改变它(没有下一行要连接)并终止处理。最后一行从不打印。
这就是为什么需要另一个命令列表的原因,就在最后一行:${s/\$//;p;}
。它仅适用于最后一行,因为它前面有行规范($
表示最后一行)。列表中的第一个命令(替代 s/\$//
)删除尾随 \
,如果有的话。第二个 (p
) 打印模式 space.
注意:如果您知道最后一行不以反斜杠结尾,您可以稍微简化一下:
sed -nE '$p;N;s/\([^\]*:)//;P;D' input_file
关于如何识别应从中删除尾随反斜杠的行的问题有点不清楚,但由于输入看起来像一组 makefile 格式的先决条件列表,其中一些行已被删除,我以 objective 为删除列表中最后一个(剩余)先决条件之后出现的反斜杠。这需要向前看下一行,因此在向前看时利用 sed
的 hold space 来存储数据会很有帮助下一行找出如何处理它。
对于该问题,这将是一个非常可靠的解决方案:
sed -nE 's/\s*(\){0,1}$/ \/; :a; /:/ { x; s/\s*\$//; p; d; }; H; $ { s/.*/:/; b a }' input
这会在保留 space 中构建每个先决条件列表,并嵌入反斜杠和换行符,然后在下一个目标列表或输入末尾到达时将其转储。
详情:
-n
选项关闭在每行后自动打印模式 space-E
选项打开扩展正则表达式sed
表达式包含几个子表达式,由分号连接:s/\s*(\){0,1}$/ \/
:确保模式 space 中的当前行以 space 和反斜杠结尾,而不向已经有一个 [=70= 的行添加第二个反斜杠]:a
: 指向脚本中的标签 'a'/:/ { x; s/\s*\$//; p; d; }
:在包含冒号的行上,交换模式并按住 spaces,从模式 space 的(新内容)中删除结尾的反斜杠,打印结果,然后开始下一个循环H
:(如果控制到达这一点)将换行符和模式 space 的内容附加到保留 space$ { s/.*/:/; b a }
:在输入触发器的最后一行通过在模式 space 中放置一个冒号并跳转到标签 'a'[=70 来转储保留 space =]- [表达式结束]:将下一行读入模式 space 并重新开始
或者,它会更准确地遵循您的要求,并避免引入前导空行,这样做:
sed -n ':a; /\$/! { p; d; }; h; :b; $ { x; s/\//; p; }; n; /:/ { x; s/\$//; p; x; b a; }; H; /\$/ b b; s/.*//; x; p' input
在最终打印之前,它也在货舱 space 中组装零件,但它以不同的方式进行:
- 它从检查模式 space 中的行是否以反斜杠结尾开始(在标签
a
处)。如果不是 (/\$/!
),则打印模式 space 并开始下一个循环。 - 否则,它将保留 space 的当前内容替换为模式 space 的内容(必须已经以反斜杠结尾),然后
- (at label
b
) 如果当前行是最后一行,则它检索 hold space 的内容,去除尾随的换行符,并打印结果 ($ { x; s/\//; p; }
).无论哪种方式, - 它尝试读取下一个输入行,如果没有(
n
)则终止。 - 如果这导致模式 space 中包含一个冒号,则打印保留 space 的内容,减去尾随反斜杠,并将控制发送回标签
a
将包含冒号的行作为新的第一行处理 (/:/ { x; s/\$//; p; x; b a; }
)。 - 否则,换行符和模式 space 的内容将附加到保留 space (
H
). - 如果模式 space 以反斜杠结尾,则控制分支返回标签
b
以考虑读取另一行 (/\$/ b b
)。 - 否则,保留 space 被打印并清除 (
s/.*//; x; p
),并且 - 如果还有更多行,则读取下一行并开始新的循环。
这对输入的性质做出了更少的假设,但有点复杂。