组合多个sed命令

时间:2012-05-31 10:25:18

标签: regex shell sed

拥有以下文件:

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

我怎样才能这样做呢?这段代码真的很无比..

先谢谢, 马利

5 个答案:

答案 0 :(得分:11)

使用-e选项。在man sed

中查找

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

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/<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选项效率更高,但我认为管道选项更方便。

答案 1 :(得分: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
  • 在保留空间(HS)中存储<td>行。 /<td/H
  • 删除除最后一行之外的所有行。 /^<\/tr/!d
  • 交换到HS。 x
  • 删除所有换行符。 s/\n//g
  • 用换行符替换第二个<td>s/<td>/\n/2
  • 删除HS中的所有文本,但插入的换行符后面的第一个非空格字段除外。 s/.*\n\(\S*\).*/\1/

答案 2 :(得分:2)

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

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

我认为你可以用sed的hold和pattern空间来​​做一些棘手的事情,以获得第二行和第四行,(我已经看到了可以通过这种方式撤消文件的双倍间距的解决方案。)

答案 3 :(得分:1)

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

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模式。

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

答案 4 :(得分:1)

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

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

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

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

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