【问题标题】:regex and sed command to match relative path filename正则表达式和 sed 命令匹配相对路径文件名
【发布时间】:2012-12-30 22:22:03
【问题描述】:

我正在努力创建一个 sed 正则表达式命令来更改这样的行:

In file included from dira/file_a.h:8, dire/file_e.h:9, and dirf/file_f.h:10,
             from dirb/file_b.h:6,
             from /existing/abs/path/dirb/file_b.cc:6:
dirc/file_c.h:88: error: 'eqn_count_t' does not name a type
dirc/file_c.h:95: error: 'wave_count_t' does not name a type
dirc/file_c.h:104: error: ISO C++ forbids declaration of 'WmHyperbolicEqnSet' with no type

到这个想要的输出

In file included from /abspaths/dira/file_a.h:8, /abspaths/dire/file_e.h:9, and /abspaths/dirf/file_f.h:10,
             from /abspaths/dirb/file_b.h:6,
             from /existing/abs/path/dirb/file_b.cc:6:
/abspaths/dirc/file_c.h:88: error: 'eqn_count_t' does not name a type
/abspaths/dirc/file_c.h:95: error: 'wave_count_t' does not name a type
/abspaths/dirc/file_c.h:104: error: ISO C++ forbids declaration of 'WmHyperbolicEqnSet' with no type

所以,

  • 只匹配以.h结尾的相对路径+文件名
  • 不匹配以正斜杠开头的行(因此已经是绝对路径)
  • 每行匹配多次出现
  • 很明显,我需要一个与 Mac OS X 的 BSD sed 命令配合使用的命令。

我想要的 regex 和 sed 命令是什么?

我正在尝试修改 gcc 输出,因为包含错误/警告的头文件会生成错误流输出,其中引用了相对路径,而不是绝对路径。使用我的 XCode IDE 调用外部构建系统,.h 文件中发生的错误不是“可点击的”。

【问题讨论】:

  • 标题与您的问题不符。 /abspath/dirb/file_b.cc不在行首,也不以.h结尾
  • 哎呀,你是对的。我不在乎行首,我会编辑标题。

标签: regex bash sed osx-mountain-lion


【解决方案1】:

上次编辑

我终于成功地创建了这样的命令,只需要 1 个命令:

sed 's/^\(.* \)\{0,1\}\([^/ ][^ ]\{1,99\}\.h\)/\1\/abspath\/\2/;' testfile.txt

为了支持多重匹配......即使在 Mac 上也是如此(来自@sudo_O 的一些简化想法):

sed -E -e :a -e 's/^(.* )?([^/][^ ]+\.h)/\1\/abspath\/\2/' -e ta testfile.txt 

但 Mac 的 sed 实现不支持 ; 作为命令分隔符。所以我们需要使用多个-e 命令字段。

为了测试它们,我使用以下命令修改了textfile.txt

sed -e '4s/^.*$/& &/' -i.bak testfile.txt

(也可以在 Mac 上工作)

有两个部分,第一个可以计数任何字符串,但以空格结尾... 0 或 1 次。第二部分不能以空格或斜杠开头,可以包含除空格以外的任何内容,并且必须以.h 结尾。如果匹配,则第一部分(包含前导空格,但如果为 0 次则可能为空)必须跟在 /abspath/ 后面,而不是第二部分。

老年人

这不行吗?

编辑针对 Mac 修改:

sed 's/ \([^/ ][^ ]\{1,99\}\.h\)/ \/abspath\/\1/;' testfile.txt

在 Mac 和 Linux 上工作相同。我已将+ 替换为{1,99}

抱歉,我没有正确阅读这个问题。这在两者上都可以正常工作:

sed 's/^\([^/ ][^ ]\{1,99\}\.h\)/\/abspath\/\1/;
     s/ \([^/ ][^ ]\{1,99\}\.h\)/ \/abspath\/\1/;' testfile.txt

【讨论】:

  • 测试了您的编辑。第 4-6 行没有变化。第 1-3 行是正确的。
  • 一个新版本提供了一个适用于整个工作的版本,在 Mac 在 Linux 上
  • 是的,这是有效的,谢谢。你能解释一些关键部分吗?为什么你必须重复一个新的s 命令?
  • 像@sudo_O一样,命令是重复的,因为我没有成功使用or可以写成:s/^\(.* \|\)...匹配两种情况:1:部分位于行中间但前面有一个空格。而 2:部分位于行首。
  • 像 @sudo_O 一样,如果您觉得这有帮助,我将不胜感激 (+1)。
【解决方案2】:

Mac 和 Linux 友好:

sed -E 's/^([^/][a-zA-Z/_]+\.h)/\/abspaths\/\1/;s/ ([^/][a-zA-Z/_]+\.h)/ \/abspaths\/\1/g' file

匹配所需的输出:

In file included from /abspaths/dira/file_a.h:8, /abspaths/dire/file_e.h:9, and /abspaths/dirf/file_f.h:10,
             from /abspaths/dirb/file_b.h:6,
             from /existing/abs/path/dirb/file_b.cc:6:
/abspaths/dirc/file_c.h:88: error: 'eqn_count_t' does not name a type
/abspaths/dirc/file_c.h:95: error: 'wave_count_t' does not name a type
/abspaths/dirc/file_c.h:104: error: ISO C++ forbids declaration of 'WmHyperbolicEqnSet' with no type

说明:

当替换不在行首时,需要两次替换来解决所需的额外空间:

s/^([^/][a-zA-Z/_]+\.h)/\/abspaths\/\1/;   # First substitution for start of line 
s/ ([^/][a-zA-Z/_]+\.h)/ \/abspaths\/\1/g  # Second for non-start of line

# Match (first substitution)
s/
^             - start of line
(             - capture group 
[^/]          - not a forward slash 
[a-zA-Z/_]+   - one or more letter, forward slash or underscore
\.h           - the extension (escaped) 
)             - end capture group 
# Replace with 
/
\/abspaths\/  - the literal string /abspaths (slashes escaped)
\1            - the captured group 
/;
# Match (second substitution)
s/
' '           - not start of line but a single space (used quotes here for space)
(             - capture group 
[^/]          - not a forward slash 
[a-zA-Z/_]+   - one or more letter, forward slash or underscore
\.h           - the extension (escaped) 
)             - end capture group 
# Replace with 
/
' '           - put the single space back
\/abspaths\/  - the literal string /abspaths (slashes escaped)
\1            - the captured group 
/g            - global flag

或者只是通过做一个替换(基于F.Hauri)答案,但是每行只能匹配一个:

sed -E 's/^(.* )?([^/][^ ]+\.h)/\1\/abspath\/\2/' file

对于多个匹配sed 支持branching

sed -E ':a;s/^(.* )?([^/][^ ]+\.h)/\1\/abspath\/\2/;ta' file

【讨论】:

  • 输出看起来不错,但对我与 @Lev 的 cmets 相关的我不起作用。我没有 -r 选项。当我这样做时()没有匹配/替换发生。有什么想法吗?
  • 谢谢,我尝试了您的编辑,以及使用 -E 且括号前没有转义的版本。没有发生匹配...
  • 关闭。我在我编辑的测试字符串上运行。在第三行,我得到from /abspaths//existing/abs/path/dirb/file_b.h:6,,所以不希望出现双重//
  • 谢谢!也为我工作。如果您可以在答案中添加一些解释,那就太好了。您最近的编辑表达式变得很长。 (另外,我想知道您的 linux 友好版本是否已经使用最新的测试输入进行了全面测试?)
  • 我已经添加了解释,如果您觉得我的回答有帮助,请 +1。
【解决方案3】:

试试这个:

sed "s|\([^\s]*\.h\)|/abspath/\1|" <testfile.txt

并忽略绝对路径:

sed "s|^\([^/][^\s]*\.h\)|/abspath/\1|" <testfile.txt

【讨论】:

  • 过度匹配,这会替换第 2 行和第 3 行(不需要重定向)。
猜你喜欢
  • 1970-01-01
  • 2010-09-19
  • 2022-10-19
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
  • 2022-11-16
  • 2016-12-11
  • 2018-05-31
相关资源
最近更新 更多