Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 199 lines (172 sloc) 4.32 KB
#!/usr/bin/env ruby
# Start Date: Sunday January 4, 2009
# Current Version: 1
# Author: Joseph Pecoraro
# Contact: joepeck02@gmail.com
# Decription: Grab certain lines of a file
# with a simple syntax (see usage). Silent
# usage useful for piping output and Pipe
# option for piping input into it
# Wrapper around functionality
class FileLines
attr_accessor :lines, :stdin
def initialize(options, filename=nil)
if filename.nil?
@stdin = true
@lines = []
while line = STDIN.gets
@lines << line
end
else
@stdin = false
@lines = File.readlines(filename)
end
@silent = false
parse_options(options)
end
def parse_options(options)
options.each do |option|
if option.match(/^-(-silent|s)$/)
@silent = true
end
end
end
def parse(args)
args.each do |arg|
# String: all
if m = arg.match( /^all$/ )
print_lines(0, @lines.size)
# Ranges: 3-7
elsif m = arg.match( /^(\d+)-(\d+)$/ )
lower = m[1].to_i
upper = m[2].to_i
lower = 1 if lower.zero? # 0 => 1
print_lines( lower-1, upper-lower+1 ) if upper >= lower
# Lookarounds: 3~7 <before_and_after>~<line>
elsif m = arg.match( /^(\d+)?~(\d+)$/ )
range = m[1].nil? ? 2 : m[1].to_i
lineno = m[2].to_i
lower = lineno-range
lower = 1 if lower < 0
upper = lineno + range
print_lines( lower-1, upper-lower+1 )
# Down: *7
elsif m = arg.match( /^\*(\d+)$/ )
lower = 1
upper = m[1].to_i
print_lines( lower-1, upper-lower+1 )
# Up: 7*
elsif m = arg.match( /^(\d+)\*$/ )
lower = m[1].to_i
upper = @lines.size
print_lines( lower-1, upper-lower+1 )
# Skipping 2:1/2 <offset>:<range>/<skip>
elsif m = arg.match( /^((\d+):)?(\d+)\/(\d+)$/ )
offset = m[2].nil? ? 1 : m[2].to_i
range = m[3].to_i
skip = m[4].to_i
print_with_skips(offset, range, skip)
# Single Lines
else
lineno = arg.to_i
lineno = 1 if lineno.zero? # 0 => 1
lineno = lineno-1 if lineno > 0 # Handle Negatives
print_line( lineno )
end
end
end
def print_lines(lower, upper)
return if lower > @lines.size
i = lower
@lines[lower, upper].each do |line|
if @silent
puts line
else
puts "[#{i+1}]: #{line}"
end
i += 1
end
end
def print_line(line)
real = line>@lines.size ? @lines.size-1 : line
if @silent
puts @lines[real]
else
if line < 0
puts "[#{real}]: #{@lines[real]}"
else
puts "[#{real+1}]: #{@lines[real]}"
end
end
end
def print_with_skips(offset, range, skip)
offset = 1 if offset < 0
range = 1 if range < 1
skip = 1 if skip < 1
cnt = 0
lineno = offset-1
skipping = false
@lines[offset-1, @lines.size].each do |line|
lineno += 1
cnt += 1
if skipping
if cnt >= skip
skipping = false
cnt = 0
end
next
else
if cnt >= range
skipping = true
cnt = 1
end
if @silent
puts line
else
puts "[#{lineno}]: #{line}"
end
end
end
end
end
# When run as as script
if $0 == __FILE__
# Pull out the Options
options = ARGV.select { |arg| arg.match(/-[a-z]/i) }
ARGV.delete_if { |arg| arg.match(/-[a-z]/i) }
# Special Handling for Pipes (don't require a filename)
is_pipe = !STDIN.tty?
# Check Cmd Line Args and Print Usage if needed
if ARGV.size <= 1 && !is_pipe
puts "usage: line [options] filename numbers"
puts
puts " options:"
puts " --silent or -s Just print the line, without [#]"
puts
puts " number formats:"
puts " 1-3 Prints lines 1 through 3"
puts " 5 Prints line 5"
puts " -1 Prints the last line, (negative)"
puts
puts " extra formats:"
puts " all Prints out all lines"
puts " ~5 Prints 2 (default) lines before and after 5"
puts " 4~10 Prints 4 lines before and after 10"
puts " *7 or 8* Prints all lines before or after the number"
puts " 5/1 Prints 5 lines, then skips 1 line..."
puts " 2:5/1 Starts at line 2, prints and skips..."
puts
exit 1
end
# Pipe Usage
if is_pipe
FileLines.new(options).parse(ARGV)
# Unreadable File
elsif !File.readable?(ARGV[0])
puts "line: file '#{ARGV[0]}' did not exist or was unreadable."
exit 1
# Run the Program
else
FileLines.new(options, ARGV[0]).parse(ARGV[1, ARGV.size])
end
end