【问题标题】:Combine multiple sed commands [duplicate]组合多个 sed 命令 [重复]
【发布时间】:2012-06-05 14:26:44
【问题描述】:

拥有以下文件:

<tr class="in">
  <th scope="row">In</th>
  <td>1.2 kB/s (0.0%)</td>
  <td>8.3 kB/s (0.0%) </td>
  <td>3.2 kB/s (0.0%) </td>
</tr>
<tr class="out">
  <th scope="row">Out</th>
  <td>6.7 kB/s (0.6%) </td>
  <td>4.2 kB/s (0.1%) </td>
  <td>1.5 kB/s (0.6%) </td>
</tr>

我想像这样获取每秒&lt;td&gt;&lt;/td&gt; 之间的值(并将其保存到文件中):

8.3
4.2

到目前为止我的代码:

# get the lines with <td> tags
cat tmp.txt | grep '<td>[0-9]*.[0-9]' > tmp2.txt

# delete whitespaces
sed -i 's/[\t ]//g' tmp2.txt

# remove <td> tag
cat tmp2.txt | sed "s/<td>//g" > tmp3.txt

# remove "kB/s (0.0%)"
cat tmp3.txt | sed "s/kB\/s\((.*)\)//g" > tmp4.txt

# remove </td> tag and save to traffic.txt
cat tmp4.txt | sed "s/<\/td>//g" > traffic.txt

#rm -R -f tmp*

我怎样才能以常见的方式做到这一点?这段代码真的很牛逼。。

提前致谢, 马利

【问题讨论】:

  • 删除空格的首选方法是tr -d [:blank:]tr -d ' \t'
  • 你可以通过避免无用的 cat 使用来节省大量的分叉:sed "..." &lt;tmpX.txt &gt;tmpY.txt 有效!
  • 不要使用 shell 工具解析 HTML 或 XML。使用更适合的 XML 技术,例如 Xpath。

标签: regex shell sed


【解决方案1】:

使用 -e 选项(如果使用 GNU sed)。来自the manual

e [command] 此命令允许从 shell 命令管道输入 进入模式空间。没有参数,e 命令执行 在模式空间中找到并替换模式空间的命令 与输出;尾随换行符被禁止。

如果指定了参数,则 e 命令将其解释为 命令并将其输出发送到输出流。命令可以运行 跨越多行,除了最后一个以反斜杠结尾。

在这两种情况下,如果要执行的命令,结果都是不确定的 包含一个 NUL 字符。

请注意,与 r 命令不同,命令的输出将是 立即打印; r 命令改为延迟输出到 当前周期结束。

所以在你的情况下你可以这样做:

cat tmp.txt | grep '<td>[0-9]*.[0-9]' \
| sed -e 's/[\t ]//g' \
-e "s/<td>//g" \
-e "s/kB\/s\((.*)\)//g" \
-e "s/<\/td>//g" > traffic.txt

你也可以这样写:

grep "<td>.*</td>" tmp.txt | sed 's/<td>\([0-9.]\+\).*/\1/g'

\+ 匹配一个或多个实例,但它不适用于非 GNU 版本的 sed。 (例如,Mac 有 BSD)

在下面@tripleee 评论的帮助下,这是我能得到的最精炼的版本,它也适用于sed 的非GNU 版本:

sed -n 's/&lt;td&gt;\([0-9]*.[0-9]*\).*/\1/p' tmp.txt

作为旁注,您也可以简单地将输出通过每个 sed 传递,而不是保存每个输出,这是我看到人们通常为临时任务所做的:

  cat tmp.txt | grep '<td>[0-9]*.[0-9]' \
    | sed -e 's/[\t ]//g' \
    | sed "s/<td>//g" \
    | sed "s/kB\/s\((.*)\)//g" \
    | sed "s/<\/td>//g" > traffic.txt

-e 选项更有效,但我猜管道选项更方便。

【讨论】:

  • 您可以取消cat,例如grep '...' tmp.txt | ...
  • 你也可以取消grepsed -e '/&lt;td&gt;[0-9]*.[0-9]/!d' -e ... tmp.txt &gt;traffic.txt
  • 多个-e 的替代方案是单个的,其连续命令由; 分隔
【解决方案2】:

[编辑] 感谢 Barton 指出错误。更正版本:

cat tmp.txt | grep td | sed 's/<td>\([0-9]\.[0-9]\).*/\1/g' > newtmp.txt
sed -n '2,${p;n;n}' newtmp.txt > final.txt; rm newtmp.txt

第一行会挑出每行 td 之后的 digit.digit 模式。

第二行从第二行开始每隔三行打印一次(有效地为您提供文件中每三行中的第二行)。

【讨论】:

  • 嗯。我刚试过这个,但sed 'd;n;d;' newtmp.txt 部分没有给出任何输出。
  • 糟糕,我在系统上尝试的命令实际上是 'n;d;n'。你说得对,'d;n;d' 没有输出……看看我是否可以用按要求工作的东西来编辑我的答案,因为 'n;d;n' 给出了每隔一行。好地方!
【解决方案3】:

您关于运行多个 sed 的问题似乎已得到解答,但 sed 是错误的工具。假设输入格式是固定的,并且&lt;tr&gt; 始终位于行首,并且您要查找的 td 标记始终在该行前面正好有 2 个空格(如果不是这种情况,则可以轻松修改此解决方案),你可以这样做:

awk -F'</?td>' '/^<tr/{i=0} /^  <td/{i++} i==2{print $2}' input-file

第一个参数告诉 awk 在&lt;td&gt;&lt;/td&gt; 上分割每一行,因此您感兴趣的数据成为第二个字段。每当&lt;tr 出现在行首时,第二个参数的第一个子句将计数器 i 重置为零。每次 &lt;td 出现在 2 个空格之后,下一个增量 i。最后打印第二个&lt;td&gt; 行的第二个字段。最后一个参数指定您的输入文件。

当然,这为您提供了&lt;td&gt; 标签之间的所有内容,我认为这不是您想要的。要获取&lt;td&gt; 和第一个空格之间的文本块,请尝试:

awk '/^<tr/{i=0} /^  <td/{i++} i==2{gsub( "<td>", ""); print $1}' input-file

【讨论】:

    【解决方案4】:

    您可以使用大括号创建一个由一个地址或一组地址操作的块:

    sed -n '/<td>[0-9]*.[0-9]/ {s/[\t ]//g; s/<td>//g; s/kB\/s\((.*)\)<\/td>//g;p}' tmp.txt
    

    我认为您可以使用 sed 的保持和模式空间做一些棘手的事情,以便获得第二行和第四行,(我已经看到可以通过这种方式撤消文件双倍间距的解决方案。

    【讨论】:

      【解决方案5】:

      这可能对你有用(GNU sed):

       sed '/^<tr/,/^<\/tr>/!d;/<td/H;/^<\/tr/!d;x;s/\n//g;s/<td>/\n/2;s/.*\n\(\S*\).*/\1/' file
      

      解释:

      • 关注开始&lt;tr&gt; 和结束&lt;/tr&gt; 标记之间的行。 /^&lt;tr/,/^&lt;\/tr&gt;/!d
      • 在保持空间 (HS) 中存储 &lt;td&gt; 行。 /&lt;td/H
      • 删除范围内除最后一行之外的所有行。 /^&lt;\/tr/!d
      • 切换到 HS。 x
      • 删除所有换行符。 s/\n//g
      • 用换行符替换第二个&lt;td&gt;s/&lt;td&gt;/\n/2
      • 删除 HS 中的所有文本,除了插入的换行符后的第一个非空格字段并打印。 s/.*\n\(\S*\).*/\1/

      【讨论】:

      • 除了\S 的使用,我在你的表达中没有看到任何GNU sed 独有的东西,它可以便携地表示为[^[:space:]]
      • @BartonChittenden 我在保守派方面犯了错误,因为我知道上述解决方案在 Linux 机器上使用 GNU sed 有效,而它可能不适用于 BSD 或 Mac 或其他任何东西。
      • 尝试使用--posix 标志运行它;这将禁用所有 GNU 扩展。
      猜你喜欢
      • 2014-08-21
      • 2017-01-01
      • 2015-01-30
      • 2016-07-31
      • 2015-01-29
      • 1970-01-01
      • 2013-12-07
      • 2011-12-01
      • 1970-01-01
      相关资源
      最近更新 更多