7

具有以下文件:

<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>

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

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*

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

提前致谢,马利

4

5 回答 5

14

使用该-e选项(如果使用 GNU sed)。从手册

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 评论的帮助下,这是我能得到的最精致的版本,它也适用于非 GNU 版本sed

sed -n 's/<td>\([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选项更有效,但我猜管道选项更方便。

于 2012-05-31T10:30:56.487 回答
3

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

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

解释:

  • 关注开始<tr>和结束</tr>标签之间的行。/^<tr/,/^<\/tr>/!d
  • <td>在保持空间 (HS) 中存储行。/<td/H
  • 删除范围内除最后一行之外的所有行。/^<\/tr/!d
  • 换成 HS。x
  • 删除所有换行符。s/\n//g
  • <td>用换行符替换 2nd 。s/<td>/\n/2
  • 删除 HS 中的所有文本,除了插入的换行符之后的第一个非空格字段并打印。s/.*\n\(\S*\).*/\1/
于 2012-05-31T12:02:24.170 回答
2

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

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

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

于 2012-05-31T12:32:50.647 回答
1

[编辑] 感谢 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 模式。

第二行从第二行开始每隔三行打印一次(这实际上为您提供了文件中每三行中的第二行)。

于 2012-05-31T10:42:25.803 回答
1

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

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

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

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

awk '/^<tr/{i=0} /^  <td/{i++} i==2{gsub( "<td>", ""); print $1}' input-file
于 2012-05-31T12:47:52.233 回答