我有一个9,000多行CSV文件traffic.csv,其中包含有关通过防火墙的流量的信息 我有第二个400行文本文件myhosts.txt,其中包含我的主机。
我想在字段'src-hostclass'和'dst-hostclass'中搜索我的主机(第二个文件)。
我的第一次尝试:
#!/usr/bin/ruby
require 'csv'
hostclass = ARGV[0]
# Load file
csv_fname = ARGV[1]
csv_text = File.open(csv_fname)
csv = CSV.parse(csv_text, :headers => true )
temp = csv.find {|row| row['src-hostclass'] == hostclass }
puts temp if temp
temp = csv.find {|row| row['dst-hostclass'] == hostclass }
puts temp if temp
(“如果临时就放温度”,那么就不会打印出空行。有没有更好的方法呢?)
然后我这样调用ruby程序:
for i in `cat myhosts.txt` ; do ./findhosts.rb $i traffic.csv ; done
这很有效,但速度很慢。
更改我的脚本以便同时读取这两个文件的最佳方法是什么?
我尝试读取这两个文件然后在内存中搜索,但这不起作用(找不到任何内容):
require 'csv'
# Load all hosts
hostclasses = File.open(ARGV[0], "r")
# Load CSV file
csv_fname = ARGV[1]
csv_text = File.open(csv_fname)
csv = CSV.parse(csv_text, :headers => true )
hostclasses.each do |hostclass|
temp = csv.find {|row| row['src-hostclass'] == hostclass }
puts temp if temp
temp = csv.find {|row| row['dst-hostclass'] == hostclass }
puts temp if temp
end
我做错了什么?
2015年5月19日更新:根据建议,我更改并简化了我的计划:
require 'csv'
require 'set'
hostclasses = File.readlines(ARGV[0]).to_set
csv_text = File.open(ARGV[1])
csv = CSV.parse(csv_text, :headers => true )
results = csv.filter {|row| hostclasses.member?(row['src-hostclass']) || hostclasses.member?(row['dst-hostclass']) }
但是运行此结果
program.rb:9:in `<main>': undefined method `filter' for #<CSV::Table mode:col_or_row row_count:10> (NoMethodError)
答案 0 :(得分:2)
您正在执行您的Ruby程序400次,每个主机执行一次。相反,尝试使程序更灵活,这样您就可以执行一次。这样,它只需要解析该9000行CSV文件一次。您可以使用Ruby而不是使用Bash脚本阅读myhosts.txt
。
另一个问题是您正在遍历9000行CSV文件以使用Array#find
查找行。这将花费O(N)时间,在这种情况下可能会很慢。相反,您应该使用index,这样您就可以在O(log(N))时间内有效地查找行。简单的Ruby哈希是一种很好的索引类型。
这是我提出并测试的脚本:
#!/usr/bin/ruby
require 'csv'
raise if ARGV.size != 2
hosts_fname, csv_fname = ARGV
row_by_src = {}
row_by_dst = {}
CSV.foreach(csv_fname, headers: true) do |row|
row_by_src[row['src-hostclass']] = row
row_by_dst[row['dst-hostclass']] = row
end
File.foreach(hosts_fname) do |host|
host = host.chomp
s = row_by_src[host] and puts s
d = row_by_dst[host] and puts d
end
答案 1 :(得分:2)
问题不在于您需要一次读取两个文件,而是在shell循环中多次读取同一文件。相反,您应该阅读一次CSV文件并构建一个包含所有主机名的Set。然后一次打印出所有行。
require 'csv'
require 'set'
hostclasses = File.readlines('myhosts.txt').to_set
results = CSV.foreach(ARGV[1], headers: true) do |row|
puts row['src-hostclass'] if hostclasses.member?(row['src-hostclass'])
puts row['dst-hostclass'] if hostclasses.member?(row['dst-hostclass'])
end