我正在尝试使用grep匹配包括整数和小数在内的所有数字,并在同一行上打印匹配(为了更容易使用gnuplot绘图)。例如,
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" | grep -E -o '\d+(\.\d+)?'
打印
100
1000
3212.97
但是如何在同一行中获得所有内容,如下所示?
100 1000 3212.97
编者注:问题的原始形式仅使用\d+
作为正则表达式,反映在一些较旧的答案中。
最终,我希望它可以使用多个输入文件,例如:
grep Throughput *.out | grep -E -o '\d+(\.\d+)?'
应该打印
100 1000 3212.97
200 3000 5444.77
300 5000 6769.32
答案 0 :(得分:1)
其他一些变体:
下面的每个例子都使用这个正则表达式:
(\d+\.\d*|\.\d+|\d+)
匹配(在一个组中)ddd.
ddd.ddd
.ddd
ddd
。如果您的小数不同,例如不想捕获.ddd
(仅十进制)变体,只需将其从正则表达式中删除。
用于一个文件/字符串
#using `paste`
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" | grep -Eo '(\d+\.\d*|\.\d+|\d+)' | paste -s -
# using echo for making the "one line"
echo $(grep -Eo '(\d+\.\d*|\.\d+|\d+)' <<< "bench-100-net-buffering1000.out:Throughput: 3212.97")
#HERESTRING and different separator
grep -Eo '(\d+\.\d*|\.\d+|\d+)' <<< "bench-100-net-buffering1000.out:Throughput: 3212.97" | paste -sd, -
#process substitution.. ;)
paste -sd ' ' <(grep -Eo '(\d+\.\d*|\.\d+|\d+)' <<< "bench-100-net-buffering1000.out:Throughput: 3212.97")
与使用bash
循环的多个文件相同。在使用ff*
作为文件名的示例中。
#Using null-term find
while IFS= read -r -d '' file; do
grep -Eo '(\d+\.\d*|\.\d+|\d+)' "$file" | paste -s -
done < <(find . -maxdepth 1 -type f -name ff\* -print0)
# or alternative - also prints filenames
while IFS= read -r -d '' file; do
echo "$file:" $(grep -Eo '(\d+\.\d*|\.\d+|\d+)' $file)
done < <(find . -maxdepth 1 -type f -name ff\* -print0)
echo Using FOR loop
for file in ff* ; do
grep -Eo '(\d+\.\d*|\.\d+|\d+)' "$file" | paste -s -
done
perl变体:
perl -0777 -nE 'say "@{[/(\d+\.\d*|\.\d+|\d+)/g]}"' ff*
还会打印文件名
perl -0777 -nE 'say "$ARGV @{[/(\d+\.\d*|\.\d+|\d+)/g]}"' ff*
也可以使用不同的字段分隔符\t
perl -0777 -nE '$"="\t";say "$ARGV @{[/(\d+\.\d*|\.\d+|\d+)/g]}"' ff*
所有perl
解决方案都使用baby-cart operator。它通常不会针对生产代码进行推荐,但对于oneliners来说是可以接受的。
演示:
perl -0777 -nE 'say "@{[/(\d+\.\d*|\.\d+|\d+)/g]}"' <<< "some-111-decimal-222.-another-333.33-only-frac-.444.txt"
输出
111 222. 333.33 .444
答案 1 :(得分:1)
我喜欢Perl中的这个解决方案 - 这也应该正确得到浮点数:
perl -ne 'print join("\t", /(\d+(?:.\d+))/g); print "\n"' files*
join
的第一个参数给出了字段分隔符
?:
创建一个所谓的非捕获组,以避免在输出中的浮点之后复制该部分 - 请参阅:https://perldoc.perl.org/perlretut.html#Non-capturing-groupings
答案 2 :(得分:1)
单输入案例
$ echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
grep -E -o '[0-9]+(\.[0-9]+)?' |
paste -sd' ' -
100 1000 3212.97
请注意,我已将\d
替换为[0-9]
,因为您没有指定平台,我已将正则表达式更改为符合POSIX标准。
grep
始终理解\d
,但GNU grep
仅使用-P
选项,BSD / macOS不支持。 paste -sd ' ' -
用空格替换换行符,以获得单行,空格分隔的数字列表。
-
表示stdin,在paste
的BSD / macOS版本中是必需的(GNU paste
可选)。-s
按顺序连接输入行。d' '
指定空格char。连接时应该用作输入行之间的分隔符(分隔符); paste
的默认值是tab char。 (\t
)。paste
优于tr '\n' ' '
,因为后者会产生尾随空格。paste
也优于column
,因为如果输出行比显示更宽,后者会插入换行符(并且总是使用\t
作为分隔符(-s
选项仅适用于-t
,此处无法使用))
也就是说,paste
不能使用多字符字符串作为固定分隔符;问题中的示例输出当前使用 2 空格作为分隔符字符串,因此如果您想实现这一点,请将paste
输出管道sed 's/ / /g
多文件输入案例:
下面的解决方案使用shell循环和2个grep
调用以及每个输入文件paste
调用;考虑使用更简洁高效的Perl solution from inferno's helpful answer。
如果您愿意假设所有匹配的行都包含3个数字,则可以使用grep
和paste
的更有效解决方案(改编自OP自己的解决方案尝试); paste
用于分别应用传递给-d
(空格,空格,换行符)的3个分隔符字符,循环:
paste -sd ' \n' <(grep -h Throughput *.out | grep -Eo '[0-9]+(\.[0-9]+)?')
对于特定于文件的输出,您必须单独处理文件(这假定给定文件中匹配行的所有数字应为输出为单行):
for file in *.out; do
grep Throughput "$file" | grep -Eo '[0-9]+(\.[0-9]+)?' | paste -sd ' ' -
done
for file in *.out
分别循环遍历所有匹配的文件。
grep Throughput "$file"
输出包含Throughput
的文件中的所有行。
| grep -Eo '[0-9]+(\.[0-9]+)?'
然后从这些行中提取数字,每个数字都打印在自己的行上。
| paste -sd ' ' -
然后用空格替换换行符,以获取每个文件的单行数字列表。
至于为什么你的方法不起作用:
grep Throughput *.out | grep -Eo '\d+(\.\d+)?'
通过管道在所有输入文件中发送匹配行的单流,因此后续命令无法知道哪些行来自哪个文件或行,不可能对每个输入文件或行的数字进行分组(在后续步骤中) - 除非您可以对每个输入行中包含的确切数量的固定数字进行假设。
答案 3 :(得分:1)
对于您的第一个简单案例,您将获得所需的输出:
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
grep -o -E '[0-9]*\.?[0-9]+' | column
输出:
100 1000 3212.97
编辑:
感谢mklement0,他指出使用paste
代替column
可能是更好的解决方案:
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
grep -o -E '[0-9]*\.?[0-9]+' | paste -s -
对于多个输入文件,我也更喜欢perl解决方案,因为它看起来相当容易和直接:
perl -nE 'say join "\t", /[0-9]*\.?[0-9]+/g' *.out
此示例使用(仅用于演示)三个相同的输入文件 file1.out , file2.out 和 file3.out 。< / p>
输出:
100 1000 3212.97
100 1000 3212.97
100 1000 3212.97
编辑(回应mklement0的评论):
要仅处理包含单词“吞吐量”的所有行,下面是一个稍微扩展的示例:
perl -nE 'say join "\t", /[0-9]*\.?[0-9]+/g if /Throughput/' *.out
答案 4 :(得分:1)
所有这些解决方案似乎都很复杂。呈现的一个不是特别有效,但是可以工作:
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: drop'
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)\\package\\'
artifactName: 'strategy'
它的作用:
1)分别从文件while read -r line
do
echo $line | grep -o "PATTERN" | tr "\n" " " ; echo
done < grep.txt
中读取每一行,并摸索模式。这可以让您拥有多种模式,而不受任何特定数字或非常特定的正则表达式的约束
2)然后,用grep.txt
删除所有不必要的换行符,将它们转换为空格(对于具有任意数量模式的每个特定行,而不是整个文件)
3)最后,tr
命令建立以移至下一行
最终得到的是完全按照要求来自同一行中echo
中同一行的模式。
答案 5 :(得分:0)
为什么不sed?简单难看的解决方案(反馈欢迎):
$ echo "bench-100-net-buffering1000.out:Throughput: 3212.97" | sed -re 's/[^0-9]+/ /g;s/ +/ /g;s/^ //'
100 1000 3212 97
或显式匹配整数和浮点数:
$ echo "bench-100-net-buffering1000.out:Throughput: 3212.97" | sed -re 's/([^0-9]+)([0-9]+|[0-9]+\.[0-9]+)/\2 /g'
100 1000 3212.97
答案 6 :(得分:0)
这是一个gnu awk
命令来获取输出:
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
awk 'n = split($0, a, /[0-9]*\.?[0-9]+/, vals) {
for (i=1; i<=n; i++)
printf "%s%s", vals[i], (i == n ? ORS : OFS)
}'
100 1000 3212.97
答案 7 :(得分:0)
根据您的问题,这是一个简单的命令,可以获得您想要获得的输出。
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" | grep -oE '[0-9]+(\.[0-9]+)?' | tr '\n' ' ' | paste -s
100 1000 3212.97
希望这有帮助!
答案 8 :(得分:0)
我真的很喜欢anubhava awk
脚本。
我希望通过更多gnu awk
功能来改进它,使其更简洁明了。
此技巧将在输入行中打印所有数字,无论多少。
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
awk 'BEGIN {FPAT="[0-9]*\\.?[0-9]+"} { # define input fields to be numbers
$1 = $1; # recalculate the input line to hold only input fields
print; # print recalculated input line
}'
或使用一根衬纸:
echo "bench-100-net-buffering1000.out:Throughput: 3212.97" |
awk 'BEGIN{FPAT="[0-9]*\\.?[0-9]+"}{$1=$1}1'