REBOL and the shell
REBOL's expressive power shines when you see what can be accomplished
with small amounts of code. The following article demonstrates the
use of REBOL to develop useful shell scripts. Using REBOL you can
enhance the capabilities of the shell with relatively little effort.
This document assumes you have some familiarity with REBOL and the
unix shell. If you have more familiarity with REBOL than unix, or the
other way around, you still may find this document informative, or
hopefully interesting.
Making a REBOL shell script |
In order to turn a REBOL script into a general shell script, place the
following "interpreter" line at the top of the script:
#!/path/to/rebol -qs
Replace "/path/to/" with the path that leads to where REBOL is
installed on your system. The interpreter line is the very first
thing in the script, preceding the REBOL header which follows on the
next line.
The flags "-qs" mean to run the script quiet (no banners are printed)
and to run the script with out security. REBOL's default security
level allows it to write and read stuff from the network as well as
reading files, but REBOL will ask permission before writing files.
However, when REBOL is running at the default security level with the
-q flag, any attempt to write a file will cause the script to
immediately quit. Therefore, lowering security on the interpreter line
is necessary only if your script is going to write files. Keep in mind
that ordinary shell scripts have no additional security measures built
in at all! The examples included in this document specify the
security lowering -s flag on the interpreter line only where
necessary.
To finish turning a REBOL script into a usable shell script it must be
executable and in your path. A convenient spot to put your scripts is
in your personal bin/ directory off your home directory. You can
check your PATH variable by doing "echo $PATH" to see that your bin/
directory is there. If it is not, you can add your bin/ directory to
your path as follows.
Under bash flavors:
export PATH=$HOME/bin/:$PATH
Under csh:
setenv PATH=$HOME/bin/:$PATH
The above line can go in your shell init script (.login, .profile,
.cshrc, .bash_rc, etc...) to make the change permanent. Finally, to
make the script executable:
chmod u+x ~/bin/script
Afterwards, you should be able to type the name of your script at the
shell and REBOL will execute it.
In the examples below, we'll include the so called "interpreter" line
pointing to /usr/local/bin/rebol and a small REBOL header.
Strip control-Ms from all files in a directory |
I've seen hackers argue for days about the simplest way to strip the
control-M's from text files that come from ms-dos. There are
certainly various terse ways to do this using sed, awk, and tr,
but here is how simple it is to do using REBOL:
#!/usr/local/bin/rebol -qs
REBOL [Title: "Strip-Ms"]
foreach file read change-dir system/options/path [
if not dir? file [write file read file]
]
REBOL writes out files in the your native operating system format, so
by simply reading in a file and writing it out those obnoxious ms-dos
control-Ms are taken care of. Presuming you installed the above
script in your path, you would visit a directory containing text files
from dos land and type in the name you gave your script (stripms
perhaps?). Be a little careful to only do this in a directory with
text files. To read and write binary files use READ/binary and
WRITE/binary. This leads to an obvious improvement of the script:
checking the suffixes of files before reading and writing them -- only
doing so for ".txt" or ".r" files, etc..
Notice that the script used system/options/path to determine where to
go. There are a few different paths found in REBOL's system object:
- system/user/home -- user's home directory
- system/script/path -- where the script is found
- system/options/path -- where you were when you invoked the script
Lowercase a directory of files. |
Sometimes you wind up with some disk or archive full of annoying all upper
case filenames. It's a snap to lowercase the whole directory with
REBOL:
#!/usr/local/bin/rebol -qs
REBOL [Title: "Lowercase em"]
foreach file read change-dir system/options/path [rename file lowercase file]
The above script works a lot like the first script we looked at. The
same approach works for many scripts that operate on the contents of a
directory.
Dump web page text and data |
Here's a small script to dump the plain text of a web page:
#!/usr/local/bin/rebol -q
REBOL [Title: "Dump Web"]
parse load/markup to-url system/script/args [
some [tag! | set x string! (prin x)]
]
If you called this script "dweb", then you'd would invoke it from the
command line like this:
dweb http://www.rebol.com
Changing this script to print out the links on a web page is
trivial:
#!/usr/local/bin/rebol -q
REBOL [Title: "Dump Web links"]
parse load/markup to-url system/script/args [
some [set x tag! (
if x: find/tail x "a href=" [print trim/with form x {<">}]
) | string!
]
]
Call it dlink, and try it out:
dlink http://slashdot.org
Passing arguments to your script |
Arguments you specify on the command line will be found in
system/options/args. Here's a little script to help see what these all
these path, home, and args are all about:
#!/usr/local/bin/rebol -q
REBOL [Title: "Rebol paths and args"]
foreach item [
system/user/home
system/script/path
system/options/path
system/options/args
][print [:item "==" mold item]]
Call the script "rargs" and put it in your bin/ directory. Change
directories to /usr/local and type 'rargs foo bar'. You should see
something like this:
system/user/home == %/home/yourname/
system/script/path == %/home/yourname/bin/
system/options/path == %/usr/local/
system/options/args == "foo bar"
If no arguments were provided the args would be set to NONE. As you
see, if you supply more than one argument, they'll actually come in as
a single string. You can break strings up into separate pieces with
this handy one liner:
parse "foo bar" none
== ["foo" "bar"]
A block containing each separate word is returned. Because the args
can sometimes be NONE you have to check first before you try to split
the args as a string:
if not none? system/options/args [args: parse system/options/args none]
That's too much writing for my tastes, so you can get away with a
little hack like this;
parse any [system/options/args ""] none
In the example above, if system/options/args is NONE you'll end up
parsing an empty string instead, yielding an empty block. No harm, no
foul, however code that comes afterwards must deal with a possible
empty block.
Note: command line arguments undergo shell expansion first, so REBOL
shell scripts can be composed with other shell expressions. For
instance, with the our rargs script from above, try these out:
rargs *
rargs $HOME
rargs `ls /`
Occasionally there is a need to do a quick math calculation, or some
fast string manipulation. REBOL allows you to do these sorts of one
liners from the command line using the --do switch:
rebol --do "print 2 * pi * 4"
But it is convenient to have that process shortened a little. Call
the next script "reb":
#!/usr/local/bin/rebol -qs
REBOL [Title: "Reb"]
random/seed now
print do system/options/args
First, the random number generator is seeded as a convenience for when
we want to use our mini REBOL expression evaluator to produce random
items. Then our script merely DOes the arguments. Using "reb" at the
command line we have many nice possibilities:
reb "1 * 2"
reb head reverse read %.
reb pick x: read %. random length? x
You may ask what use is picking a random file? Perhaps each day 'xv'
picks a random image from a directory and puts it on your root window,
or perhaps you have a cron job that periodically changes your web
pages.
reb random 100
reb send friend@somewhere.com '"Hey!!"' 0
Or more complex expressions:
reb "do fib: func [n m][if n > 50 [quit] fib m probe n + m] 0 1"
reb "0.0.0.255 + read dns://yahoo.com"
reb `find . | grep -c foo`" * 4"
The last example above would print 4 times the number of files found
recursively from the present directory that contain the string "foo".
Using shell quoting rules you can mix shell substitutions with other
REBOL expressions to evaluate.
Much of the time the arguments can be provided as is, but sometimes
they need to be quoted to avoid unwanted shell substitutions. Beyond
the basic shell quoting rules, we also have to quote lines that
contain dashes or pluses which normally signal command line flags for
REBOL.
Sometimes you are on a system that does not have mail delivered
locally and can only be retrieved via pop. I've always been a big fan
of the FROM command (FRM on some systems). FROM allows you to see a
quick summary of your mail box, who each message is from and the
subject line. If there's something interesting you might want to read
your mail, otherwise you go on about what ever it is you were doing.
Here is an example of doing a FROM through pop which also demonstrates
how easy network protocols are to manipulate in REBOL:
#!/usr/local/bin/rebol -q
REBOL [Title: "POP FROM"]
me: "account"
pass: ask/hide "Password? "
server: "pop.server.dom"
pop: open rejoin [pop:// me ":" pass "@" server]
forall pop [
message: import-email first pop
print [index? pop message/from tab message/subject]
]
close pop
This pop FROM script will first ask you for your password, hiding the
keys you type. You COULD put your password right in the script, but
that might not be too safe in a multiuser environment. Of course, you
need to plug in your account and your pop server in the appropriate
places.
If you have multiple pop accounts, it would be easy for this script to
be changed to go FROM them all together.
In the same vein as the previous script, here is another network
convenience script. While working on a web site, you have a local
directory where you work on the source files. Periodically you make
some changes to your local copies of the web site files and you want to
get them up to the remote web server. No need to fire up some
dedicated application to move those files over, just roll a quick
little script that takes all the hassle out of it.
#!/usr/local/bin/rebol -qs
REBOL [Title: "to-web"]
me: "account"
pass: ask/hide "Password? "
server: "ftp.server.dom"
foreach file read change-dir system/options/path [
if not dir? file [
write/binary rejoin [
ftp:// me ":" pass "@" server "/public_html/" file
] read/binary probe file
]
]
The script is hard wired to upload every file in the directory you
call it from to public_html/ on your remote server. Creating a
recursive directory upload is also fairly easy.
If you end up getting network time outs using ftp, try adding the
following line to the begining of your script:
system/schemes/ftp/passive: true
Setting passive to true in REBOL's ftp scheme will turn on REBOL's ftp
passive mode. In passive mode, REBOL will connect back to the ftp site
for the data transmission, instead of the other way around as it
normally goes. If you are behind a firewall, ftp servers will likely
not be able to establish the data connection to you, and hence the
timeouts.
Using REBOL/view it is a snap to create a quick and dirty reminder
program:
#!/usr/local/bin/rebol -q
REBOL [Title: "Reminder"]
if found? args: system/options/args [
set [time message] load/next args
wait time view layout compose [
text red (copy message) font [size: 20]
]
]
Call the script "remind" and put it in bin/ directory. Here is an
example of invoking the script
remind 2 update the database &
Two seconds later your message should pop up on your screen. If you
want to wait a few hours you'd specify the time in a REBOL's time
format:
remind 4:00:00 it is four hours later &
LOAD/next was used to extract the time value from from the first
position in the arguments.
REBOL/view can display jpeg, gif and bmp files. With REBOL/view you
can make a simple script to view an image. When you click on the
image or hit space bar while having the window in focus, the window
will close.
#!/usr/local/bin/rebol -q
REBOL [Title: "View an image"]
change-dir system/options/path
if found? args: system/options/args [
view layout [image (to-file args) #" " [quit]]
]
The #" " is the space character and it defines a shortcut for the
image. Following the space character is a block that is executed when
that shortcut is pressed or when the image is clicked. REBOL/view's
layout dialect is a very simple way to create dynamic GUIs.
I called the above script "eye". Moving into a directory with an
image file, the script is invoked like this:
eye picture.jpg
As expected, the image pops up in a window.
That's just the beginning |
These preceding little examples don't begin to scratch the surface of
REBOL's capabilities. It is very easy to start building time saving
REBOL scripts for the shell that make your life much easier.
|