23.3 The Exec Function

23.3.1 Synopsis

exec (prog_file_name);
exec (prog_file_name, arguments);

23.3.2 Description

Execute prog_file_name and redirect its standard output within output file; prog_file_name is the name of the file storing the script or the executable and can be any valid expression:

If the latter syntax is used, you can pass a set of arguments to the command. arguments can be one or more expressions separated by commas. Every language has its own facility to access arguments; as an example shell scripts use $1, $2 etc. and $n represents the number of parameters.

Remember that file names must be relative to the directory storing the file where the exec () function occurs. In practice, imagine to change to this directory and invoke the script manually: if you need to prefix script name with ./ (depends on your PATH environment variable), you'll also need a trailing ./ when invoking the script from the exec () function. The script must also be executable (type chmod u+x script_name from the shell).

Warning: If you use Cows-mkgen to create makefiles, prog_file_name can only be provided as a string constant (e.g. "foo.sh"); otherwise, Cows-mkgen will miss the dependency and raise a warning. Of course, Cows will correctly include the file, since it can handle complex expressions, but if the included file is changed, make won't update output file. For further informations see Section 24.1.

Technically speaking, you could also provide prog_file_name via the inputfile () and outputfile () functions and Cows-mkgen will be able to track dependencies. However, I don't think it can be useful since you should write a script which is both a valid Cows file and a valid script in another language!

Provided the conditions above, Cows-mkgen knows that your file depends on the external script. Of course, if the script introduce further dependencies, Cows-mkgen can't track them (otherwise, it should be able to interpret every scripting language!).

There are two ways to handle these situations; the easy way is to use the __DEP__ directive and explicitly tell Cows-mkgen about the new dependency (Section 6.6.4):

File hello.sh

#!/bin/sh
echo Hello World
cat foo.txt

File hello.cws

<cows> // __DEP__ foo.txt
exec ("hello.sh");
</cows>

This is not a good way: if you include hello.sh from many files, and you add a dependency, you'll need to search and update every __DEP__ directive. G-Cows was born to avoid things like this.

A better way consists in creating an additional makefile (Section 6.4.3); this compels you to learn makefile syntax but can save you a lot of time if you modify the dependencies introduced by your script: you'll simply need to change Makefile.add and run Cows-mkgen again in order to update Makefile.

File hello.sh

#!/bin/sh
echo Hello World
cat foo.txt

File hello.cws

<cows>
exec ("hello.sh");
</cows>
File Makefile.add
hello.sh: foo.txt
    touch hello.sh

This is a hack since this additional rule does nothing but touching the script. However, it suffices our needs: every cws file whose output depends on hello.sh will depend on foo.txt too. So, when you change foo.txt, these pages will be updated. Use it carefully, only if you know what you're doing; if you're in doubt you should probably use the __DEP__ directive.

When you execute an external script via the exec Function, you face the same problem of the include () statement (Section 17.2.1). Suppose a shell script (let's say /site-dir/tools/img-list.sh) is called via the exec Function from file /site-dir/main/foo/bar/somefile.cws. Script img-list.sh has to loop over site images but it's executed from the directory storing the .cws file: we need a way to tell the script how to reach other files.

Inside Cows the linkadjust () function allows to achieve this goal (Section 20.3); in order to provide a similar feature for scripts and programs, Cows exports an environment variable call LA (uppercase letters), storing a string consisting in a number of ../ sequences given by the number of directories traveled to reach the generated file from the root directory. This is the same string returned by linkadjust () so look at Section 20.3 for further informations.

If you've never used environment variables before, simply think about them as variables which are passed to your scripts and programs from the Operating System or from the invoking programs. Every language has a different way to access them: as an example the value of a variable called LA can be displayed as $LA in shell scripts, can be accessed with the os.environ[] dictionary in Python, with the getenv() function in C and C++ and so on.

23.3.3 Example

Create a file called infos.sh with the following content (it's a simple shell script which displays current date and some informations about the Operating System):

infos.sh

#!/bin/sh
echo -n "Today's date is: "
date
echo "My Operating System is: "
uname -a
echo "<img alt=\"Description\" src=\"$LAimages/img.jpg\">"

Now, you have to make it executable:

chmod u+x infos.sh

The best way to test it and see if it works is to run it from the command line:

./infos.sh
Today's date is: Thu May  1 12:45:21 CEST 2003
My Operating System is:
FreeBSD veganstar.local 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov  3
09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386
<img alt="Description" src="/img.jpg">

Please note that environment variable $LA has not been expanded: this variable is set by Cows before running the script so it's not available when the script is run from the command line.

Now let's create a simple Cows script called exec.cws:

exec.cws

<h1>Executing an external Script</h1>
<cows>
  exec ("./infos.sh");
</cows>

Run Cows on it and look at the result:

$ cows exec.cws exec.html
$ cat exec.html

<h1>Executing an external Script</h1>
Today's date is: Thu May  1 12:46:50 CEST 2003
My Operating System is:
FreeBSD veganstar.local 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov  3
09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386
<img alt="Description" src="/img.jpg">

23.3.4 Example 2

Modify file infos.sh so that it handles an optional arguments with the name of an image.

infos.sh

#!/bin/sh
echo -n "Today's date is: "
date
echo "My Operating System is: "
uname -a
if [ $# -eq 1 ];
  then echo "<img alt=\"Description\" src=\""$LA"images/$1\">";
fi

If you have modified the old file it's already executable, if you created from scratch type:

chmod u+x infos.sh

Again, let's run it from the command line:

./infos.sh
Today's date is: Thu May  1 14:30:26 CEST 2003
My Operating System is:
FreeBSD veganstar.local 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov  3
09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386

./infos.sh guitar.jpg
Today's date is: Sun Oct 19 18:08:46 CEST 2003
My Operating System is:
FreeBSD veganstar.local 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov  3
09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386
<img alt="Description" src="images/guitar.jpg">

Now let's modify the calling Cows script execute.cws:

<h1>Executing an external Script</h1>
<cows>
  exec ("./infos.sh", "guitar.jpg");
</cows>

Run Cows on it and look at the result:

$ cows exec.cws exec.html
$ cat execute.html

<h1>Executing an external Script</h1>
Today's date is:
My Operating System is:
<img alt="Description" src="images/guitar.jpg">

This manual can be downloaded from http://www.g-cows.org/.