如何获得grep每条输出行的长度
How can I get the length of each output line of grep
我对 bash 脚本编写还很陌生。
我有一个要解析的网络跟踪文件。跟踪文件的一部分是(两个数据包):
[continues...]
+---------+---------------+----------+
05:00:00,727,744 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|
+---------+---------------+----------+
05:00:00,727,751 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|56|00|00|3a|01|
[continues...]
对于每个数据包,我想打印时间戳和数据包的长度(|0 header 后下一行的十六进制值),因此输出将如下所示:
05:00:00.727744 20 bytes
05:00:00.727751 24 bytes
我可以在 bash 中使用 grep 分别获取带有时间戳的行和数据包:
times=$(grep '..\:..\:' $fileName)
packets=$(grep '..|..|' $fileName)
但之后我无法使用单独的输出行。整个结果连接在两个变量 "times" 和 "packets" 中。我怎样才能得到每个数据包的长度?
P.S。如果能真正解释如何进行 bash 编程,而不仅仅是举例,我们将不胜感激。
你真的不想用你的 shell 做这样的事情。
您想编写一个真正的解析器来理解输出所需信息的格式。
对于快速而肮脏的 hack,您可以这样做:
perl -wne 'print "$& " if /^\d\S*/; print split(/\|/)-2, " bytes\n" if /^\|..\|/'
好吧,用普通的旧 shell...
你可以这样得到线的长度:
line="|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|"
wc -c<<<$line
62
那一行有六十二个字。将每个字符视为 |00
,其中 00
可以是任何数字。在这种情况下,最后会有一个额外的 |
。另外,wc -c
包括末尾的 NL
。
因此,如果我们取 wc -c
的值并减去 2,我们将得到 60
。如果我们将其除以 3,我们将得到 20
,即字符数。
好的,现在我们需要一个小循环,找出不同的行,然后解析它们:
#! /bin/bash
while read line
do
if [[ $line =~ ^[[:digit:]]{2} ]]
then
echo -n "${line% *}"
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
then
length=$(wc -c<<<$line)
((length-=2))
((length=length/3))
echo "$length bytes"
fi
done < test.txt
PURE BASH解决您的问题!
你是一个初学者 Bash 程序员,你不知道发生了什么......
让我们一步一步来:
在 BASH 中遍历文件的常用方法是使用 while read
循环。这结合了 while
和 read
:
while read line
do
echo "My line is '$line'"
done < test.txt
test.txt
中的每一行都被读入 $line
shell 变量。
我们看下一个:
if [[ $line =~ ^[[:digit:]]{2} ]]
这是一个if
声明。始终使用 [[ ... ]]
括号,因为它们解决了 shell 插值问题。另外,他们有更多的权力。
=~
是正则表达式匹配。 [[:digit:]]
匹配任何数字。 ^
将正则表达式锚定到行的开头,而 {2}
意味着我只需要其中两个。这表示如果我匹配以两位数字开头的行(这是您的时间戳行),请执行此 if
子句。
${line% *}
是模式过滤器。 %
表示将 (glob) 最小的 glob 模式匹配到右侧并从我的 $line
变量中过滤它。我用它从我的行中删除 ETHER
。 -n
告诉 echo
不要做 NL。
让我们以我的 elif
为例,这是一个 else if 子句。
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
同样,我正在匹配一个正则表达式。这个正则表达式以 (The ^
) a |
开头。我必须在前面加一个反斜杠,因为 |
是一个神奇的正则表达式字符,而 \
会破坏魔法。现在只是一根管子。然后,后面跟着两位数字。请注意,这会跳过 |0
但会捕获 |00
.
现在,我们要做一些计算:
length=$(wc -c<<<$line)
$(...)
表示执行包含的命令并将其重新替换回行中。 wc -c
计算字符数,<<<$line
是我们计算的字符数。这给了我们 62
个字符。我们必须减去 2,然后除以 3。那是接下来的两行:
((length-=2))
((length/=3))
((...))
让我可以进行基于整数的数学运算。第一个从 $length
中减去 2,然后将其除以 3
。现在,我可以回应:
echo "$length bytes"
这就是我们对这个问题的纯粹 Bash 回答。
我对 bash 脚本编写还很陌生。 我有一个要解析的网络跟踪文件。跟踪文件的一部分是(两个数据包):
[continues...]
+---------+---------------+----------+
05:00:00,727,744 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|
+---------+---------------+----------+
05:00:00,727,751 ETHER
|0
|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|56|00|00|3a|01|
[continues...]
对于每个数据包,我想打印时间戳和数据包的长度(|0 header 后下一行的十六进制值),因此输出将如下所示:
05:00:00.727744 20 bytes
05:00:00.727751 24 bytes
我可以在 bash 中使用 grep 分别获取带有时间戳的行和数据包:
times=$(grep '..\:..\:' $fileName)
packets=$(grep '..|..|' $fileName)
但之后我无法使用单独的输出行。整个结果连接在两个变量 "times" 和 "packets" 中。我怎样才能得到每个数据包的长度?
P.S。如果能真正解释如何进行 bash 编程,而不仅仅是举例,我们将不胜感激。
你真的不想用你的 shell 做这样的事情。
您想编写一个真正的解析器来理解输出所需信息的格式。
对于快速而肮脏的 hack,您可以这样做:
perl -wne 'print "$& " if /^\d\S*/; print split(/\|/)-2, " bytes\n" if /^\|..\|/'
好吧,用普通的旧 shell...
你可以这样得到线的长度:
line="|00|03|a0|09|5c|1c|00|10|07|df|a4|20|08|00|45|00|00|38|e7|55|"
wc -c<<<$line
62
那一行有六十二个字。将每个字符视为 |00
,其中 00
可以是任何数字。在这种情况下,最后会有一个额外的 |
。另外,wc -c
包括末尾的 NL
。
因此,如果我们取 wc -c
的值并减去 2,我们将得到 60
。如果我们将其除以 3,我们将得到 20
,即字符数。
好的,现在我们需要一个小循环,找出不同的行,然后解析它们:
#! /bin/bash
while read line
do
if [[ $line =~ ^[[:digit:]]{2} ]]
then
echo -n "${line% *}"
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
then
length=$(wc -c<<<$line)
((length-=2))
((length=length/3))
echo "$length bytes"
fi
done < test.txt
PURE BASH解决您的问题!
你是一个初学者 Bash 程序员,你不知道发生了什么......
让我们一步一步来:
在 BASH 中遍历文件的常用方法是使用 while read
循环。这结合了 while
和 read
:
while read line
do
echo "My line is '$line'"
done < test.txt
test.txt
中的每一行都被读入 $line
shell 变量。
我们看下一个:
if [[ $line =~ ^[[:digit:]]{2} ]]
这是一个if
声明。始终使用 [[ ... ]]
括号,因为它们解决了 shell 插值问题。另外,他们有更多的权力。
=~
是正则表达式匹配。 [[:digit:]]
匹配任何数字。 ^
将正则表达式锚定到行的开头,而 {2}
意味着我只需要其中两个。这表示如果我匹配以两位数字开头的行(这是您的时间戳行),请执行此 if
子句。
${line% *}
是模式过滤器。 %
表示将 (glob) 最小的 glob 模式匹配到右侧并从我的 $line
变量中过滤它。我用它从我的行中删除 ETHER
。 -n
告诉 echo
不要做 NL。
让我们以我的 elif
为例,这是一个 else if 子句。
elif [[ $line =~ ^\|[[:digit:]]{2} ]]
同样,我正在匹配一个正则表达式。这个正则表达式以 (The ^
) a |
开头。我必须在前面加一个反斜杠,因为 |
是一个神奇的正则表达式字符,而 \
会破坏魔法。现在只是一根管子。然后,后面跟着两位数字。请注意,这会跳过 |0
但会捕获 |00
.
现在,我们要做一些计算:
length=$(wc -c<<<$line)
$(...)
表示执行包含的命令并将其重新替换回行中。 wc -c
计算字符数,<<<$line
是我们计算的字符数。这给了我们 62
个字符。我们必须减去 2,然后除以 3。那是接下来的两行:
((length-=2))
((length/=3))
((...))
让我可以进行基于整数的数学运算。第一个从 $length
中减去 2,然后将其除以 3
。现在,我可以回应:
echo "$length bytes"
这就是我们对这个问题的纯粹 Bash 回答。