UPDATE July 24, 2010: This post is now totally obsolete. See this post instead.
Recently, I’ve realized that the Autolily script I made was just one solution to a larger class of problems — that of calling a specific program on a target source/text file repeatedly every time you change the source. So, I’ve modified it slightly to make it accept any program name, so that the general format is: autocall [program] [file]. The source code is below:
#!/usr/bin/ruby
#===============================================================================================================#
# Program name: Autocall #
# Author: Shinobu (zuttobenkyou.wordpress.com) #
# Date: March 2010 #
# LICENSE: PUBLIC DOMAIN #
# #
# This program takes 2 or 3 arguments; the first 2 is the command and file, while the third optional arg is the #
# delay b/n each possible execution of the command. By default this delay is 1 second (it checks if the file has#
# been modified every second) #
# #
# Place this script somewhere, like in ~/scripts #
# Then, open up a terminal and call it like so: ~/scripts/autocall.rb [program] [file] #
# #
# You might want to do a "sudo ln -s" of autocall.rb to one of your system's $PATH directories (e.g., /usr/bin) #
# to avoid typing out the path to autocall.rb every time you use it. Continuing the example from above, #
# something like "sudo ln -s ~/scripts/autocall.rb /usr/bin/autocall" should do (make sure that #
# /usr/bin/autocall does not exist already, as the above comman will overwrite that file if it exists). #
# #
# Now you can just do: #
# #
# autocall [command] [file] #
# #
# from anywhere in your system! #
# #
# To exit, press CTRL-C. #
#===============================================================================================================#
if ARGV.size > 1
file_data_orig = ""
call = ARGV.shift
file = ARGV.shift
delay = 1
if ARGV.size > 0
delay = ARGV.shift.to_i
end
pathsize = file.split("/").size
ls_call = "ls --full-time"
# make sure that the "file" variable is a filename, and not mixed with its path
if pathsize > 1
path_to_file = file.split("/").first(pathsize - 1).join("/")
file = file.split("/").last
ls_call << " #{path_to_file}" # modify our `ls` command to reflect relative location of file
end
`#{ls_call}`.split("\n").each do |line|
if line.split(/\s/).last == file
file_data_orig = line
break
end
end
file_data_new = ""
# enter infinite loop -- keep compiling the given file if it has changed in the past 1 second
while true
# detect the file size and also timestamp
lsarr = `#{ls_call}`.split("\n")
lsarr.shift # get rid of the first line, since that is the size of all the files in the directory
# find our file from ls's output!
lsarr.each do |line|
if line.split(/\s/).last == file
file_data_new = line
break
end
end
# if there is any change detected, run given command on it
if file_data_orig != file_data_new
puts "\n\e[1;38;5;226mautocall: change detected @ #{Time.now.ctime} in file `#{file}'; invoking `#{call}'...\e[0m\n"
if pathsize > 1
`#{call} "#{path_to_file}/#{file}"`
else
`#{call} "#{file}"`
end
file_data_orig = file_data_new
end
sleep delay
end
else
puts "Usage: autocall [command] [file]\n"
end
I can think of at least 1 other time you would want to use this script aside from editing LilyPond files — when editing LaTeX files. For me, I use currently use autocall to call a program that converts text files intelligently to HTML files. You could further edit the source to let it pass along command line options to autocall as well, and not just the program name (I will probably do this myself if the situation presents itself in the future).