Shell Scripting with a Distributed Twist: Using the Sleep Scripting Language

 in
Learn a Perl-like language whose scripts move around your network.

which is equal to this Java statement:

System.out.println("Hello World");

When calling into Java, the message is the name of a method or field that belongs to the object. Arguments are converted to Java types as necessary, and some conversions are automatic. Nearly anything will convert to a string. However, a string will not convert to an int. Casting is possible, but I don't cover that topic here.

Now that you know a little about the Sleep language, it helps to see it in action. Next, I present several scenarios and Sleep-based solutions to them.

Filesystem Fun (the Biggest File)

My home directory has many files. I'm a digital packrat, and I'm always low on disk space. I really have no idea what is on my disk. To help, I wrote a script to find the largest files within a directory and its subdirectories:

global('$size $file @files %sizes');

sub processFile
{

This script creates a data structure of files and their sizes, sorts it, and presents the results to the user. The &processFile function does most of the work, and it expects a file as an argument:


   if (-isDir $1)
   {
      filter(&processFile, ls($1));
   }

If the argument is a directory, the &ls function will provide the contents of the directory as an array. &filter expects a function and an array as arguments. &filter calls the function on each item in the array. I use &filter to call &processFile on the argument's subdirectories and files:


   else if (lof($1) > (1024 * 1024))
   {
      %sizes[$1] = lof($1);
   }
}

The hash %sizes stores each filename and size. The key is the filename, and the size is the value. The &lof function returns the length of a file in bytes. I ignore files smaller than 1MB in size. I have so many files that this script exhausts the memory of Java before finishing. I could set Java to use a larger heap size with java -Xmx1024M -jar sleep.jar. Below, I chose to fix my script:

processFile(@ARGV[0]);

I call &processFile on the first command-line argument to kick off the script. When this function returns, the %sizes hash will contain an entry for each file in the specified directory and its subdirectories:


@files = sort({ return %sizes[$2] <=> %sizes[$1]; },
                                  keys(%sizes));

The &sort function processes the keys of %sizes and places them in order from largest to smallest size. Much like Perl, Sleep's &sort can use any criteria given by an anonymous function:

foreach $file (sublist(@files, 0, 50))
{
   $size = lof($file);
   println("$[20]size $file");
}

This script ends with a foreach loop to print out the 50 largest files.

And, lo and behold! I solved my problem. I found four copies of a Christmas movie I made on my Macintosh three years ago. Thanks to the script, I recovered several gigabytes of disk space.

Local Processes (PS. I Love You)

Recently, I had to watch this movie about a guy who sent letters to his wife after he passed away. I'm not really into the romantic-morbid genre; however, I thought I could show the people in my life how much I care about them. Here is a script that sends a random fortune to someone every 24 hours:

include("sendemail.sl");

while (1)
{
   sendemail($to => "rsmudge@gmail.com",
      $from => "raffi@hick.org",
      $subject => "P.S. I love you",
      $message => "This made me think of you:\n\n" .
                  join("\n", `fortune`)
   ); 

   # sleep for 24 hours
   sleep(24 * 60 * 60 * 1000);
}

I use `fortune` to execute the fortune command and collect its output into an array. Then, I combine this with the rest of the message body to make a thoughtful message. This script uses the $variable => value syntax to pass named arguments to &sendemail.

Backticks are one way to execute a process. I show the other way in the sendemail.sl code.

Sending E-Mail

I use the sendmail program to send e-mail. The sendemail.sl file contents are:

sub sendemail
{
   local('$handle');
   $handle = exec("/usr/sbin/sendmail -t $to");

Sleep executes processes with the &exec function. Scripts interact with processes as if they were files. As an aside, you can pass arguments with spaces to &exec. Use an array instead of a string. For example, exec(@("/usr/sbin/sendmail", "-t", $to)) would work in this example:

   println($handle, 
"TO: $to
FROM: $from
SUBJECT: $subject
$message");

Here, I send the e-mail message to the sendmail process over STDIN. Later in this article, I cover how to use Sleep for distributed tasks. Don't combine this e-mail example with that—I don't like spammers:

   closef($handle);
}

The last step is to close the handle. Having successfully automated my personal life, let's turn our attention to work matters.

______________________