【问题标题】:Changing FS in awk to match anything that isn't a file path在 awk 中更改 FS 以匹配不是文件路径的任何内容
【发布时间】:2018-06-15 17:14:55
【问题描述】:

我正在尝试使用 awk 从程序的输出中提取文件路径。这是我第一次使用 awk,我听说它对这种事情有好处,所以我点击了 GNU 手册:https://www.gnu.org/software/gawk/manual/gawk.html(awk 符号链接到我机器上的 gawk)

我正在尝试更改 FS 以使分隔符匹配任何不是文件路径的内容。我在输入中硬编码了 2 个文件路径的情况下对此进行了尝试:

awk -F '[^(\\/.)*]' '{print $1; print $2}'

我认为[^(\\/.)*] 会将 FS 设置为匹配任何与文件路径不匹配的文本。我认为括号会阻止正则表达式被视为单个字符,例如[^abcd]。路径可以随心所欲,因此是星号。这不起作用。

我的输入看起来像这样:

a whole bunch of random garbage oooh! a file /opt/dir/file and perhaps some more garbage and another file! /usr/local/bin

我希望这样的输出:

/opt/dir/file
/usr/local/bin

我将在 Bash 变量中捕获此预期输出。

有人知道如何正确执行此操作吗?如果我通过了--posix 命令,这是否正确也会有所帮助。注意:垃圾中可以存储任意数量的文件路径。

【问题讨论】:

  • 如何区分文件路径和其他垃圾?您正在寻找的模式到底是什么?
  • @melpomene 文件路径的格式都是/dir1/dir2/dir3/file。垃圾是程序输出的无关信息,可以包括版本号等,但绝不包括任何看起来像文件路径的东西,除非它实际上打印了文件路径。我的目标是让垃圾成为字段分隔符,这样我就可以遍历字段并打印它们,或者对它们做一些事情。
  • 我不明白您所说的“看起来像文件路径”是什么意思。 a whole bunch of random garbage oooh! a file 是有效的目录名称。您认为它看起来不像文件路径,但我确实如此(并且 unix 同意)。所以你能做的最好的事情就是匹配一些有效目录名称的子集,我试图弄清楚这个子集到底是什么。
  • @melpomene 所有路径都从根目录开始,因此/dir1/dir2/dir3/file 将是一个路径,但garbage out put that isnt a file path /dir1/dir2/dir3/file some more crap /dir1/dir2/dir3/file2 仅包含两个文件路径。与任意长的/ 序列以及它们之间的文本不匹配的所有内容都不是路径。回想起来,路径将以空格结尾,所以/dir1/dir2/dir3/file
  • @melpomene 将要部署的系统没有 perl。

标签: regex bash awk


【解决方案1】:

当您想从某些文本中提取特定模式时,请使用 grep。查找所有包含斜线的单词:

grep -o '[^[:blank:]]*/[^[:blank:]]*'

使用 GNU grep 的 pcre 模式更容易阅读:

grep -oP '\S*/\S*'

其中\S\s(空格)的补码

【讨论】:

  • 文件名中的空格是完全有效的。此答案将无法正确匹配包含空格/制表符/换行符/等的文件名。但是,我怀疑您的输出中不需要这样的文件名。
  • 所以这两种解决方案都匹配任意数量的非空白(使用预定义的字符类,对吗?),一个文字斜线,然后是任意数量的非空白?你是对的,文件名不会有空格。我应该提到的东西:/为什么不需要转义斜线?
  • 正如我评论詹姆斯的回答,斜线不是正则表达式中的特殊字符。特殊字符包括.,*,+,[等。斜杠只需要在像 sed 这样的语言中注意,其中“搜索和替换”命令使用斜杠(默认情况下)作为分隔符。
  • 谢谢!我认为 grep 和 awk 之间可能存在差异,但我突然想到 awk 手册可能已经说明了两者之间的正则表达式相似之处。
  • 从您的 regex(7) 手册页开始。然而,令人沮丧的是,不同的工具使用不同风格的正则表达式。
【解决方案2】:

使用 GNU awk 和 RT

$ awk 'BEGIN{RS="([^ ]*/[^ ]*)+"}{print RT}' file
/opt/dir/file
/usr/local/bin
[here be a nasty empty line]

RT # 与记录分隔符RS 表示的文本匹配的输入文本。每次读取记录时设置。

编辑:您还可以将 GNU awk 的 splitseps 一起使用(注意 \/,因为 /...\/.../):

$ awk ' {
    split($0,a,/([^ ]*\/[^ ]*)+/,seps)
    for(i in seps)
        print seps[i]
}' file
/opt/dir/file
/usr/local/bin

【讨论】:

  • RS 是记录分隔符,但 RT 是什么?我没有在预定义变量部分看到它。为什么要更改记录分隔符?我不完全理解正则表达式。它匹配除空格、任意次数和正斜杠以外的任何开头的内容,我在手册中无法识别,然后再次匹配任何不是空格的任意次数。跨度>
  • 很酷的解决方案。与其将垃圾设为 FS,不如将所需的文本设为 RS。中间那个斜线是什么意思?它被解释为文字斜线?我认为那必须逃脱?
  • 是的,在这种情况下是字面意思。如果它被转义,它将产生:awk: cmd. line:1: warning: escape sequence '\/' treated as plain '/'.
  • @Ungeheuer,纯斜线在正则表达式中没有特殊含义。对于 sed 命令s/re/text/,是的,斜杠必须被转义才能不被视为命令分隔符,但不是在这里。
  • @James 将 {print RT} 更改为 RT{print RT} 以避免说“讨厌的空行”。你真的应该在正则表达式中使用\S[^[:space:]] 而不是[^ ],否则当文件路径之间有制表符时它会失败。
猜你喜欢
  • 2014-06-27
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 2018-11-28
  • 2014-02-25
  • 1970-01-01
  • 2019-11-06
  • 2014-07-28
相关资源
最近更新 更多