[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are a few builtin macros in m4
that allow you to run shell
commands from within m4
.
Note that the definition of a valid shell command is system dependent.
On UNIX systems, this is the typical /bin/sh
. But on other
systems, such as native Windows, the shell has a different syntax of
commands that it understands. Some examples in this chapter assume
/bin/sh
, and also demonstrate how to quit early with a known
exit value if this is not the case.
13.1 Determining the platform | ||
13.2 Executing simple commands | ||
13.3 Reading the output of commands | ||
13.4 Exit status | ||
13.5 Making temporary files |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Sometimes it is desirable for an input file to know which platform
m4
is running on. GNU m4
provides several
macros that are predefined to expand to the empty string; checking for
their existence will confirm platform details.
Each of these macros is conditionally defined as needed to describe the
environment of m4
. If defined, each macro expands to the empty
string. For now, these macros silently ignore all arguments, but in a
future release of M4, they might warn if arguments are present.
When GNU extensions are in effect (that is, when you did not
use the ‘-G’ option, see section Invoking m4),
GNU m4
will define the macro __gnu__
to
expand to the empty string.
$ m4 __gnu__ ⇒ __gnu__(`ignored') ⇒ Extensions are ifdef(`__gnu__', `active', `inactive') ⇒Extensions are active |
$ m4 -G __gnu__ ⇒__gnu__ __gnu__(`ignored') ⇒__gnu__(ignored) Extensions are ifdef(`__gnu__', `active', `inactive') ⇒Extensions are inactive |
On UNIX systems, GNU m4
will define __unix__
by default, or unix
when the ‘-G’ option is specified.
On native Windows systems, GNU m4
will define
__windows__
by default, or windows
when the
‘-G’ option is specified.
On OS/2 systems, GNU m4
will define __os2__
by default, or os2
when the ‘-G’ option is specified.
If GNU m4
does not provide a platform macro for your system,
please report that as a bug.
define(`provided', `0') ⇒ ifdef(`__unix__', `define(`provided', incr(provided))') ⇒ ifdef(`__windows__', `define(`provided', incr(provided))') ⇒ ifdef(`__os2__', `define(`provided', incr(provided))') ⇒ provided ⇒1 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Any shell command can be executed, using syscmd
:
Executes shell-command as a shell command.
The expansion of syscmd
is void, not the output from
shell-command! Output or error messages from shell-command
are not read by m4
. See section Reading the output of commands, if you need to process the
command output.
Prior to executing the command, m4
flushes its buffers.
The default standard input, output and error of shell-command are
the same as those of m4
.
By default, the shell-command will be used as the argument to the
‘-c’ option of the /bin/sh
shell (or the version of
sh
specified by ‘command -p getconf PATH’, if your system
supports that). If you prefer a different shell, the
configure
script can be given the option
‘--with-syscmd-shell=location’ to set the location of an
alternative shell at GNU m4
installation; the
alternative shell must still support ‘-c’.
The macro syscmd
is recognized only with parameters.
define(`foo', `FOO') ⇒ syscmd(`echo foo') ⇒foo ⇒ |
Note how the expansion of syscmd
keeps the trailing newline of
the command, as well as using the newline that appeared after the macro.
The following is an example of shell-command using the same
standard input as m4
:
$ echo "m4wrap(\`syscmd(\`cat')')" | m4 ⇒ |
It tells m4
to read all of its input before executing the wrapped
text, then hand a valid (albeit emptied) pipe as standard input for the
cat
subcommand. Therefore, you should be careful when using
standard input (either by specifying no files, or by passing ‘-’ as
a file name on the command line, see section Invoking m4), and also invoking subcommands via syscmd
or esyscmd
that consume data from standard input. When standard input is a
seekable file, the subprocess will pick up with the next character not
yet processed by m4
; when it is a pipe or other non-seekable
file, there is no guarantee how much data will already be buffered by
m4
and thus unavailable to the child.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If you want m4
to read the output of a shell command, use
esyscmd
:
Expands to the standard output of the shell command shell-command.
Prior to executing the command, m4
flushes its buffers.
The default standard input and standard error of shell-command are
the same as those of m4
. The error output of shell-command
is not a part of the expansion: it will appear along with the error
output of m4
.
By default, the shell-command will be used as the argument to the
‘-c’ option of the /bin/sh
shell (or the version of
sh
specified by ‘command -p getconf PATH’, if your system
supports that). If you prefer a different shell, the
configure
script can be given the option
‘--with-syscmd-shell=location’ to set the location of an
alternative shell at GNU m4
installation; the
alternative shell must still support ‘-c’.
The macro esyscmd
is recognized only with parameters.
define(`foo', `FOO') ⇒ esyscmd(`echo foo') ⇒FOO ⇒ |
Note how the expansion of esyscmd
keeps the trailing newline of
the command, as well as using the newline that appeared after the macro.
Just as with syscmd
, care must be exercised when sharing standard
input between m4
and the child process of esyscmd
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
To see whether a shell command succeeded, use sysval
:
Expands to the exit status of the last shell command run with
syscmd
or esyscmd
. Expands to 0 if no command has been
run yet.
sysval ⇒0 syscmd(`false') ⇒ ifelse(sysval, `0', `zero', `non-zero') ⇒non-zero syscmd(`exit 2') ⇒ sysval ⇒2 syscmd(`true') ⇒ sysval ⇒0 esyscmd(`false') ⇒ ifelse(sysval, `0', `zero', `non-zero') ⇒non-zero esyscmd(`echo dnl && exit 127') ⇒ sysval ⇒127 esyscmd(`true') ⇒ sysval ⇒0 |
sysval
results in 127 if there was a problem executing the
command, for example, if the system-imposed argument length is exceeded,
or if there were not enough resources to fork. It is not possible to
distinguish between failed execution and successful execution that had
an exit status of 127, unless there was output from the child process.
On UNIX platforms, where it is possible to detect when command execution is terminated by a signal, rather than a normal exit, the result is the signal number shifted left by eight bits.
dnl This test assumes kill is a shell builtin, and that signals are dnl recognizable. ifdef(`__unix__', , `errprint(` skipping: syscmd does not have unix semantics ')m4exit(`77')')dnl syscmd(`kill -9 $$') ⇒ sysval ⇒2304 syscmd() ⇒ sysval ⇒0 esyscmd(`kill -9 $$') ⇒ sysval ⇒2304 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Commands specified to syscmd
or esyscmd
might need a
temporary file, for output or for some other purpose. There is a
builtin macro, mkstemp
, for making a temporary file:
Expands to the quoted name of a new, empty file, made from the string
template, which should end with the string ‘XXXXXX’. The six
‘X’ characters are then replaced with random characters matching
the regular expression ‘[a-zA-Z0-9._-]’, in order to make the file
name unique. If fewer than six ‘X’ characters are found at the end
of template
, the result will be longer than the template. The
created file will have access permissions as if by chmod =rw,go=,
meaning that the current umask of the m4
process is taken into
account, and at most only the current user can read and write the file.
The traditional behavior, standardized by POSIX, is that
maketemp
merely replaces the trailing ‘X’ with the process
id, without creating a file or quoting the expansion, and without
ensuring that the resulting
string is a unique file name. In part, this means that using the same
template twice in the same input file will result in the same
expansion. This behavior is a security hole, as it is very easy for
another process to guess the name that will be generated, and thus
interfere with a subsequent use of syscmd
trying to manipulate
that file name. Hence, POSIX has recommended that all new
implementations of m4
provide the secure mkstemp
builtin,
and that users of m4
check for its existence.
The expansion is void and an error issued if a temporary file could not be created.
The macros mkstemp
and maketemp
are recognized only with
parameters.
If you try this next example, you will most likely get different output for the two file names, since the replacement characters are randomly chosen:
$ m4 define(`tmp', `oops') ⇒ maketemp(`/tmp/fooXXXXXX') ⇒/tmp/fooa07346 ifdef(`mkstemp', `define(`maketemp', defn(`mkstemp'))', `define(`mkstemp', defn(`maketemp'))dnl errprint(`warning: potentially insecure maketemp implementation ')') ⇒ mkstemp(`doc') ⇒docQv83Uw |
Unless you use the ‘--traditional’ command line option (or
‘-G’, see section Invoking m4), the GNU
version of maketemp
is secure. This means that using the same
template to multiple calls will generate multiple files. However, we
recommend that you use the new mkstemp
macro, introduced in
GNU M4 1.4.8, which is secure even in traditional mode. Also,
as of M4 1.4.11, the secure implementation quotes the resulting file
name, so that you are guaranteed to know what file was created even if
the random file name happens to match an existing macro. Notice that
this example is careful to use defn
to avoid unintended expansion
of ‘foo’.
$ m4 define(`foo', `errprint(`oops')') ⇒ syscmd(`rm -f foo-??????')sysval ⇒0 define(`file1', maketemp(`foo-XXXXXX'))dnl ifelse(esyscmd(`echo \` foo-?????? \''), ` foo-?????? ', `no file', `created') ⇒created define(`file2', maketemp(`foo-XX'))dnl define(`file3', mkstemp(`foo-XXXXXX'))dnl ifelse(len(defn(`file1')), len(defn(`file2')), `same length', `different') ⇒same length ifelse(defn(`file1'), defn(`file2'), `same', `different file') ⇒different file ifelse(defn(`file2'), defn(`file3'), `same', `different file') ⇒different file ifelse(defn(`file1'), defn(`file3'), `same', `different file') ⇒different file syscmd(`rm 'defn(`file1') defn(`file2') defn(`file3')) ⇒ sysval ⇒0 |
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by root on March 13, 2013 using texi2html 1.82.