# System (shell) commands
Executing system commands is one of the most important features of ABS, as it allows mixing the convenience of the shell with the syntax of a modern programming language.
Commands are executed with the `command`
syntax,
which resemble Bash's syntax to execute commands in a subshell:
date = `date` # "Sun Apr 1 04:30:59 +01 1995"
As you can see, the return value of a command is a simple string -- the output of the program. If the program was to encounter an error, the same string would hold the error message:
date = `dat` # "bash: dat: command not found"
It would be fairly painful to have to parse strings
manually to understand if a command executed without errors;
in ABS, the returned string has a special property ok
that
checks whether the command was successful:
if `ls -la`.ok {
echo("hello world")
}
# Executing commands in background
Sometimes you might want to execute a command in
background, so that the script keeps executing
while the command is running. In order to do so,
you can simply add an &
at the end of your script:
`sleep 10 &`
echo("This will be printed right away!")
You might also want to check whether a command
is "done", by checking the boolean .done
property:
cmd = `sleep 10 &`
cmd.done # false
`sleep 11`
cmd.done # true
If, at some point, you want to wait for the command
to finish before running additional code, you can
use the wait
method:
cmd = `sleep 10 &`
echo("This will be printed right away!")
cmd.wait()
echo("This will be printed after 10s")
If you ever want to terminate a running command, you can
use the kill
method.
cmd = `sleep 10 &`
cmd.done # false
cmd.kill()
cmd.done # true
Also note that when an exec()
command string terminates with an &
,
the exec(command)
function will terminate immediately after launching
the command which will run independently in the background.
This means that the command must either terminate on its own or be killed
using pkill
or similar. This way an ABS script can launch a true daemon
process that may operate on its own outside of ABS. For example you can
reboot a remote computer via ssh without interacting with it:
exec("ssh user@host.local 'sudo reboot' &")
# Interpolation
You can also replace parts of the command with variables
declared within your program using the $
symbol:
file = "cpuinfo"
x = `cat /proc/$file`
echo(x) # processor: 0\nvendor_id: GenuineIntel...
or interpolation within an exec(command)
cmd = args(2)
filename = args(3)
exec("sudo $cmd $filename")
and if you need $
literals in your command, you
simply need to escape them with a \
:
`echo $PWD` # "" since the ABS variable PWD doesn't exist
`echo \$PWD` # "/go/src/github.com/abs-lang/abs"
# Using a different shell
By default, ABS uses bash -c
to execute commands; on Windows
it instead uses cmd.exe /C
.
You can specify which shell to use by setting the environment
variable ABS_COMMAND_EXECUTOR
:
`echo \$0` # bash
env("ABS_COMMAND_EXECUTOR", "sh -c")
`echo \$0` # sh
# Alternative $() syntax
Even though the use of backticks is the standard recommended
way to run system commands, for the ease of embedding ABS also
allows you to use the $(command)
syntax:
$(basename $(dirname "/tmp/make/life/easy")) // "easy"
Commands that use the $()
syntax need to be
on their own line, meaning that you will not
be able to have additional code on the same line.
This will throw an error:
$(sleep 10); echo("hello world")
# Executing commands without capturing I/O
It is also possible to execute a shell command without capturing its
input or output using the exec(command)
function. This allows long running
or interactive programs to be run using the terminal's Standard IO
(stdin, stdout, stderr). For example:
exec("sudo visudo")
would open the default text editor in super user mode on the /etc/sudoers file.
Unlike the normal backtick command execution syntax above,
the exec(command)
function call does not return a result string unless it fails.
Therefore, the exec(command)
may be the last command executed in a script
file leaving the executed command in charge of the terminal IO until it
terminates.
For example, an ABS script might be used to marshall the command line args for an interactive program such as the nano editor:
$ cat abs/tests/test-exec.abs
# marshall the args for the nano editor
# if the filename is not given in the args, prompt for it
# if the file is located outside the user's home dir, invoke sudo nano filename
cmd = 'nano'
filename = arg(2)
homedir = env("HOME")
while filename == '' {
echo("Please enter file name for %s: ", cmd)
filename = stdin()
}
if filename.prefix('~/') || filename.prefix(homedir) {
sudo = ''
} else {
sudo = 'sudo'
}
# execute the command with live stdIO
exec("$sudo $cmd $filename")