使用awk和sed消除不需要的输出

时间:2012-03-28 09:23:07

标签: linux sed awk command

从以下命令中我如何消除

之前出现的所有行
 Owner     RepoName             CreatedDate

EDIT命令:

find /opt/site/ -name '.log.txt' | xargs cat | awk '{$NF=""; print $0}' | sed '1i Owner RepoName CreatedDate' | column -t

输出

find: Filesystem loop detected; `/nfs/.snapshot/nightly.4' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
find: Filesystem loop detected; `/nfs/.snapshot/nightly.5' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
find: Filesystem loop detected; `/nfs/.snapshot/nightly.6' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
Owner     RepoName             CreatedDate
val        abc                  Fri          Mar  16  17:01:07  PDT
p1         repo_pc              Wed          Mar  21  11:34:42  PDT
New        fm                   Mon          Mar  19  00:15:51  PD 

所需的输出仅为:

Owner     RepoName             CreatedDate
val        abc                  Fri          Mar  16  17:01:07  PDT
p1         repo_pc              Wed          Mar  21  11:34:42  PDT
New        fm                   Mon          Mar  19  00:15:51  PD 

5 个答案:

答案 0 :(得分:3)

那些发现错误将在stderr上,因此完全绕过你的链,你会想要用2>/dev/null重定向错误,尽管这会阻止你在find命令中看到任何其他错误。

find /opt/site/ -name '.log.txt' 2>/dev/null | xargs cat | awk '{$NF=""; print $0}' | xargs sed "/Filesystem/d" | sed '1i Owner RepoName CreatedDate' | column -t

一般情况下,如果遇到这样复杂的命令,你应该在遇到错误时将其分解,以便找出问题所在的位置。

让我们拆分这个命令,看看它在做什么:

find /opt/site/ -name '.log.txt' 2>/dev/null - 找到/ opt / site / named .log.txt

下的所有文件

xargs cat - 一个接一个地获取所有内容

awk '{$NF=""; print $0}' - 删除最后一栏

xargs sed "/Filesystem/d" - 将每个条目视为文件,并从这些文件的内容中删除包含Filesystem的所有行。

sed '1i Owner RepoName CreatedDate' - 在第一行插入所有者RepoName CreatedDate

column -t - 将给定数据转换为表格

我建议建立命令,并在每个阶段检查输出是否正确。

你的命令有几件令人惊讶的事情:

  1. 查找文件正好是.log.txt而不是扩展名。
  2. 第二个xargs调用 - 将.log.txt文件的内容转换为文件名。

答案 1 :(得分:0)

您可以通过在第一个管道之前将2> / dev / null附加到you命令的find部分来消除find的错误输出。 [编辑:这是最好的方式,我已经投票道格拉斯了,因为他先在这里;)

但是如果你真的想用sed或awk(想不出为什么?),你可以修改你的awk脚本以跳过以'find:'开头的行:

awk '/^find:/ {next;} {$NF=""; print $0}' 

答案 2 :(得分:0)

可悲的是,您似乎正在使用csh或tcsh,其中将标准错误与标准输出重定向很困难。否则道格拉斯的答案就会奏效。但试试这个:

(find /opt/site/ -name '.log.txt' | xargs cat | awk '{$NF=""; print $0}' | sed '1i Owner RepoName CreatedDate' | column -t > output) >&/dev/null

注意围绕命令的大部分的parens。在那些parens中是一个重定向,将标准输出发送到一个名为“output”的文件,而不是发送到您的终端(根据您的意愿命名它 - 或者如果您真的想看到它,请用output替换/dev/tty在你的终端)。在这些parens之外是一个重定向,将剩余的错误消息发送到/dev/null

对于可怕贝壳的长寿,整件事情是一篇悲惨的评论。

答案 3 :(得分:0)

下一个sed命令应该完成这项工作(将它与输入文件或管道一起使用):

sed -n '/^Owner/,$ p'

说明:

-n             # Disable auto-print.
/^Owner/       # From a line beginning with 'Owner'...
$              # ...until end of input...
p              # print

答案 4 :(得分:0)

这完全可以通过Awk脚本实现...

#!/usr/bin/awk -f

BEGIN {
  for (i = 1; i < ARGC; i++) {
    if (ARGV[i] ~ "^--from=") {
      _from = substr(ARGV[i], 8)
      delete ARGV[i]
    }
  }

  if (!_from) {
    print "No '--from' argument provided!" > "/dev/stderr"
  }
}


{

  if (_flag) {
    print $0
  } else if ($0 ~ _from) {
    _flag = 1
    print $0
  }

}
  

注意;上面的脚本是根据from-till.awk(从--from--till搜索表达式中打印出来)改编而成的,因此可能需要针对此特定用例调整添加的命令行选项和变量名

...允许将文件用作输入...

head-trimmer.awk --from="^Owner" file-path.txt

...或重定向,例如EOF或管道...

head-trimmer.awk --from="^Owner" <<'EOF'
find: Filesystem loop detected; `/nfs/.snapshot/nightly.4' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
find: Filesystem loop detected; `/nfs/.snapshot/nightly.5' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
find: Filesystem loop detected; `/nfs/.snapshot/nightly.6' has the same device number and inode as a directory which is 2 levels higher in the filesystem hierarchy.
Owner     RepoName             CreatedDate
val        abc                  Fri          Mar  16  17:01:07  PDT
p1         repo_pc              Wed          Mar  21  11:34:42  PDT
New        fm                   Mon          Mar  19  00:15:51  PD
EOF

...和应该将其解析为类似...

Owner     RepoName             CreatedDate
val        abc                  Fri          Mar  16  17:01:07  PDT
p1         repo_pc              Wed          Mar  21  11:34:42  PDT
New        fm                   Mon          Mar  19  00:15:51  PD

... Awk脚本可以更轻松地扩展和/或适应其他用例,正确使用它意味着可以消除对其他程序的不必要调用。

应该可以从管道中消除sedcolumn,并提供一些提示


在{strong>所有输入的开头和结尾处运行的带有Awk的BEGINEND ,例如文件列表,因此对于构建标题和列映射非常有用


在Awk中使用whilegetline可以解析命令的输出...

#!/usr/bin/awk -f

BEGIN {
  for (i = 1; i < ARGC; i++) {
    if (ARGV[i] ~ "^--directory=") {
      _directory = substr(ARGV[i], 13)
      delete ARGV[i]
    }
    if (ARGV[i] ~ "^--name=") {
      _name = substr(ARGV[i], 8)
      delete ARGV[i]
    }
    # ... perhaps add other args to parse
  }

  # ... build/print header maybe

}


{

  cmd = "find " _directory " -name " _name " 2>/dev/null"
  while (( cmd | getline _line ) > 0) {
    print "_line ->", _line
    # ... do some fancy formatting, use a built-in, or another command
    #     to build desired column output from find results
  }
  close(cmd)

  # ...

}
  

当试图编写一个Bash脚本,它只是一个带有一些自定义解析的命令的包装程序时,这可能非常方便。


有很多方便的内置Awk函数(GAwk则更多),例如。 splitlength,并且可以通过Awk脚本中的function关键字添加更多内容。


数组 / 字典变量也可以在Awk中使用,例如...

BEGIN {
  for (i = 1; i < ARGC; i++) {
    if (ARGV[i] ~ "^--from=") {
      _custom_args["from"] = substr(ARGV[i], 8)
      delete ARGV[i]
    } else if (ARGV[i] ~ "^--till=") {
      _custom_args["till"] = substr(ARGV[i], 8)
      delete ARGV[i]
    }
  }
}


{
  # ...
}
  

但是(如果我没记错的话)应该避免使用诸如 _something[0,1] 之类的多维数组,因为在Awk中,这种情况实际上是 _something["0,1"]


使用Awk将列打印为格式良好的表有些棘手,但也可以通过printf格式设置选项来实现...

#!/usr/bin/awk -f

BEGIN {
  printf("%-8s %-13s %s\n", "Owner", "RepoName", "CreatedDate")
}
  

基本上,%-8s告诉Awk至少保留8个字符,而不管字符串"Owner"%-13s保留13和{ {1}}告诉Awk在字符串的右/末端用分隔符填充较长的字符串。


-printf结合使用以防止更长的刺痛可能是有用的...

%.<n>

如果您被卡住,请随时发表评论,我将尝试使用更多技巧再次进行介绍。