From: Robert Klemme Date: 2009-12-06T21:30:09+09:00 Subject: Re: awk to ruby 1.9 On 05.12.2009 10:07, Panagiotis Atmatzidis wrote: > On 04 2009, at 10:27, David Masover wrote: > >> On Friday 04 December 2009 02:09:35 pm Panagiotis Atmatzidis wrote: >>> The output though parses lines the following: >>> >>> 2009-11-19 00:31:29,928 fail2ban.actions: WARNING [ssh-ipfw] Ban >>> 203.169.139.171 >>> I would like to use the output from the cli: "$ grep Ban fail2ban.log|awk >>> -F "Ban" '{print $2}'" > I have found this code snippet, with a slight change runs under ruby1.9 > > def ip(filename) > ips = [] > File.read(filename).lines.to_a.each do |place| > sf = 0 > while sfn = place.index(/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/,sf) > sf = sfn + 3 > ips << $& > end > end > return ips > end > It seems that is making things unnecessary complicated (there is a superfluous to_a in there etc.). That should be sufficient for your case: require 'set' def ips(file_name) ips = Set.new # [] if you do want dups File.foreach file_name do |line| ip = line[/Ban\s+(\d{1,3}(?:\.\d{1,3}){3})/, 1] and ips << ip end ips end Note: I am using a Set here in order to avoid reporting duplicates. If you want duplicates just use an array as indicated above. Other than that this is what the code does: File.foreach iterates a file line by line. The expression line[/.../, 1] extracts capturing group 1 of the regexp if it matches. Otherwise you get nil. Then I use that information immediately with "and" to decide whether to add to the set or not. The regular expression is not as selective as the one you have found in terms of the IP but it does match the "Ban" so it should be safer (after all, you know the format of the file). Also, the piece you found seems to iterate while in your file there is at most one IP per line (if your example covers all options). Plus, the iteration with index is awkward and inefficient. In Ruby you would rather do place.scan /.../ do |ip| ips << ip end But as I said, scanning is not necessary if there is at most a single IP per line. And, last but not least a more modular solution in which you do not need an Array or Set but rather use Ruby's block feature to deal with individual IPs: def ban_ips(file_name) File.foreach file_name do |line| ip = line[/Ban\s+(\d{1,3}(?:\.\d{1,3}){3})/, 1] and yield ip end end With that you can do ban_ips "fail2ban.log" do |ip| printf "found IP %-15s\n", ip end or ips = [] ban_ips "fail2ban.log" do |ip| ips << ip end puts "found #{ips.size} IPs" Welcome to the wonderful world of Ruby! Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/