In m4, how do you include a file that has an environment variable in its name?

StackOverflow https://stackoverflow.com/questions/5346119

  •  27-10-2019
  •  | 
  •  

Question

I want to include a file based relative to my sandbox base directory inside of my m4 text without using the -I switch.

So far, I have figured out how to grab the environment variables using a sys call:

define(MODEL_ROOT,`syscmd(`printf $MODEL_ROOT')')dnl

Next, I want to include a file based off that environment variable:

include(MODEL_ROOT/sw/lib/m4_macros/foreach2.m4)

In total, I have:

define(MODEL_ROOT,`syscmd(`printf $MODEL_ROOT')')

MODEL_ROOT

MODEL_ROOT/sw/lib/m4_macros/foreach2.m4

include(MODEL_ROOT/sw/lib/m4_macros/foreach2.m4)

Which prints:

/home/ross/sandbox

/home/ross/sandbox/sw/lib/m4_macros/foreach2.m4

/home/ross/sandboxforeach_example.m4:7: m4: Cannot open /sw/lib/m4_macros/foreach2.m4: No such file or directory

I know that the normal syntax for includes is

include(`file.m4')

But if I quote MODEL_ROOT/sw/lib/m4_macros/foreach2.m4, then m4 like:

[...]
include(`MODEL_ROOT/sw/lib/m4_macros/foreach2.m4')

m4 complains:

[...]
foreach_example.m4:7: m4: Cannot open MODEL_ROOT/sw/lib/m4_macros/foreach2.m4: No such file or directory

How does one include a file with an environment variable in its path?

Was it helpful?

Solution

I think you need to use esyscmd instead of syscmd. esyscmd reads command line output.

OTHER TIPS

As the other answer mentions, you have to use the GNU extension esyscmd to be able to retrieve the output of the command. The syscmd macro just prints directly to stdout, ignoring all macros and diverts.

That is why it looked like MODEL_ROOT was working everywhere else: it was, but only in very simple situations where m4 didn't need to deal with its output.

However, regarding quoting:

  • include(MODEL_ROOT/sw/lib/m4_macros/foreach2.m4)
  • include(`MODEL_ROOT/sw/lib/m4_macros/foreach2.m4')

This should have the quotes moved:

include(MODEL_ROOT`/sw/lib/m4_macros/foreach2.m4')

The quotes prevent expansion of the MODEL_ROOT macro, so they must not enclose it here (where you want it to be expanded). It is "proper" to quote the rest of the string, because it's not something you will want to be expanded by macros.


As an aside, a more robust way to get an environment variable from the shell would be something like:

define(`HOME', esyscmd(`printf \`\`%s\'\' "$HOME"'))

That will avoid problems caused by macro names, percentage signs, backslashes, glob characters, or whitespace in the environment variable's value. The only differences between this and your solution are the addition of \`\`%s\'\' and quotes around the variable.

A caveat: esyscmd will always have its output expanded as a macro, so it can be difficult to keep it truly sanitized. Even though I'm using quote symbols above, it will still trip up if those quote symbols exist in the environment variable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top