From Schmid.wiki
Jump to: navigation, search

Ruby and the System

Environment:

ENV.class => Object # not Hash, but has some of the same methods
ENV['OS']
ENV.has_key?('OS')

Arguments:

ARGV.class => Array
ARGV[0] # first argument
$0      # name of script

System commands:

begin
    x = `something`
rescue Errno::ENOENT
    $stderr.puts("no such command")
rescue
    $stderr.puts("unknown error")
end

Common taks:

if system("command") then
    puts("command was executed successfully")
end
Dir.chdir "/path/to/some/dir"

Common file tasks - fileutils:

require 'fileutils'
include FileUtils
mkdir_p "/path/to/some/dir"

Do Something For All Files in Dir

Renaming Files

Output script for renaming all files:

require 'find'
Find.find '.' do |f|
   if f =~ /regex/ then
       puts "mv '#{f}' '#{f.gsub(/regex/, "replacement")}'"
   end
end

Use like this:

ruby createRenameScript.rb|tee /tmp/rename.sh|less

If all is OK in the script, run it:

. /tmp/rename.sh

Zero Padding

require 'find'

# find largest number length in file names
largestNumberLength = 0
Find.find '.' do |f|
    if f =~ /.*?(\d+)\..*/ then
        numberLength = f.sub(/.*?(\d+)\..*/,'\1').length
        largestNumberLength = numberLength if numberLength > largestNumberLength
    end
end

# rename files to include zero padding, e.g. file1.jpg => file001.jpg
Find.find '.' do |f|
    if f =~ /.*?(\d+)\..*/ then
        numberLength = f.sub(/.*?(\d+)\..*/,'\1').length
        if numberLength < largestNumberLength then
            zeroPadding = '0' * (largestNumberLength-numberLength)
            puts "move \"#{f}\" \"#{f.gsub(/(\d+)\./, "#{zeroPadding}\\1.")}\""
        end
    end
end

Change Content

Simple example:

require 'find'
Find.find '.' do |f|
   if f =~ /.*\.txt$/ then
       puts "Modifying file: #{f}"
       infile = File.new(f, "r")
       lines = infile.readlines ; infile.close

       (0..lines.length).each do |linenumber|
	   line = lines[linenumber]
	   if line =~ /first line/ then
	       lines.insert(linenumber+1, "following after first line")
	   end
	   line.gsub!(/something/, "something else")
       end

       # outfile = File.new(f, "w")
       # lines.each do |line| outfile.puts(line) end
   end
end

Change content of all files (careful with this):

require 'find'
require 'ftools'
require 'fileutils'

files_to_change = /.*/
regex = /(some|a lot of)\s+text/
replacement = '\1 more text'

Dir.chdir(ARGV[0])
dir_name = File.basename(ARGV[0])
new_dir_name = dir_name + ".changed"
dir_with_changed_files = "../" + new_dir_name
created_dir_with_changed_files = false
Find.find '.' do |file_name|
   if File.stat(file_name).file? and file_name =~ files_to_change then
       new_file_name = dir_with_changed_files + "/" + file_name

       # read all lines from file
       infile  = File.new(file_name, "r")
       lines = infile.readlines ; infile.close

       # check if any lines should be changed
       change = false
       lines.each do |line|
           if line =~ regex then change = true ; break end
       end

       if change then
           if not created_dir_with_changed_files then
               if not File.exist?(dir_with_changed_files) then
                   puts "creating #{new_dir_name}"
                   FileUtils::mkdir(dir_with_changed_files)
                   created_dir_with_changed_files = true
               else
                   if File.stat(dir_with_changed_files).directory? then
                       puts "reusing old #{new_dir_name}"
                       created_dir_with_changed_files = true
                   else
                       puts "#{new_dir_name} exists but is not a directory."
                       exit
                   end
               end
           end

           # write modified lines to newfile
           puts "creating #{new_dir_name}/#{File.basename(file_name)}"
           outfile = File.new(new_file_name, "w")
           lines.each do |line|
               outfile.puts line.gsub(regex, replacement)
           end
       end
   end
end
if created_dir_with_changed_files then
    puts "view changes using\n diff -r #{ARGV[0]} #{ARGV[0]}.changed"
    puts "apply changes using\n cp -r #{ARGV[0]}.changed/* #{ARGV[0]}"
end

One-Liners

Execute a Single Command

$ ruby -e "puts 2**16"
65536

Regex Search

require 'find'
Find.find '..' do |f|
   puts "#{f}" if f =~ /regex/ then
end

or the really short one:

find|ruby -ne'puts$_ if~/search/i'  <- case-insensitive search

Filter

A bit like sed. Places while gets; ...; print; end around the code

$ ruby -pe '$_.upcase!'
ruby 1.8
RUBY 1.8
$ ruby -pe 'gsub(/[1-9]/,"x")'
ruby 1.8
ruby x.x
$ ruby -pe 'gsub(/[1-9]/,"x").upcase!'
ruby 1.8
RUBY X.X

Mass-renaming:

ls|ruby -ne 'if ~/regex/ then puts "mv "+$_.strip+" "+$_.downcase end' |tee /tmp/rename.sh
                \____/             \____________________________/           \____________/
                   |                             |                                 |
       only look at matching files         output this               source this afterwards if ok

filter files in-place (like sed -i)

$ ruby -pi -e "gsub(/search/, 'replace')" *.txt
$ ruby -pi.bak -e "gsub(/search/, 'replace')" *.txt   # with backup

Complex Example

example:

$ ruby -pe '$_=~/^#.*/?$_:$_=gsub(/[1-9]*:.*$/,"").split(".").collect{|x|x.capitalize}.join(" ")'
stdin:                 stdout:
# kommentar            # kommentar
svend.hansen4: hej     svend hansen
...