4

I have a file that begins with a few "include" statements, like this:

include foo.xyz
include bar.xyz
do c
do d

Suppose the file foo.xyz contains do a and the file bar.xyz contains do b. How can I write a script that replaces each include file.xyz statement with the contexts of file.xyz? I want to end up with the following output:

do a
do b
do c
do d

I tried the solution in "Replace string with contents of a file using sed" but there the filename is hard-coded. I would like the filename to be dynamically generated, depending on the include argument.

  • @don_crissti Yes, they're all of the form include filename.xyz where filename.xyz is assumed to be in the current directory. – John Wickerson Nov 11 '15 at 15:26

3 Answers3

4

Here's a simple recursive version with awk. You must create a script in your PATH with

#!/bin/bash
awk '
$1=="include" && NF>=2 {
   system("'$0' " $2)
   next
}
{print}' "$@"

It assumes filenames have no special chars (including spaces) in them. The awk checks the first word for include, then calls the script to process the file given as 2nd word. Other lines are printed. Note that the $0 here is outside the single quotes of the awk, so is a shell $0, ie the script name.

meuh
  • 51,383
  • Thank you so much. Is the idea that by putting the script in PATH, then calling the script via the system call, you can resolve include statements transitively? If so, that's jolly clever! – John Wickerson Nov 11 '15 at 15:32
  • 1
    Yes, it is to simplify includes within includes. Of course, if you know you dont have any, a simple cat inside the system() will do. – meuh Nov 11 '15 at 15:35
2
grep -n '^include ' yourfile |
sed  -e 's/:[^ ]* /{ r /'    \
     -e 'G;s/.$/&b&}/'       |
sed -nf - -ep       yourfile

that should work. grep finds those include line numbers, the first sed modifies its output to line-number-specific [num]{r fname\nb\n} commands, and the second sed prints every line which isn't one of those the first scripts for it.

{   for   x in  a b
    do    echo "do $x">"file$x.xyz"
          echo "include file$x.xyz"
    done; printf "do %s\n"  c d
}   >yourfile
cat  yourfile

include filea.xyz
include fileb.xyz
do c
do d

That just generates your sample and shows yourfile.

And this works:

grep -n '^include ' yourfile |
sed  -e 's/:[^ ]* /{ r /'    \
     -e 'G;s/.$/&b&}/'       |
sed -nf - -ep       yourfile

do a
do b
do c
do d

the read files can be any size, though, of course, and are not restricted to the single line. The filenames themselves are restricted to a single line, though, but are otherwise more or less unrestricted (depending on your sed - some older seds had arbitrary name-length limits, but most used today don't).

mikeserv
  • 58,310
0
cat  $(grep '^include' file.txt | cut -d" " -f2); grep -v '^include' file.txt

First command will grep the line starting with include in file.txt, cut the file name part and then cat it.

Next command will print all lines except line starting with include in file.txt

jijinp
  • 1,401