2

I am trying to concatenate the contents (a block of code) of a file called query.sql in a README.md document, that already contains some text. However, I would also like this block of code to be preceded by a heading "Model answer".

In other words, if my README.md file was currently structured as

# Title

Some text here.

the new file should look like

# Title

Some text here.

## Model answer

```sql
Contents of query.sql
```

The subtitle ## Model answer is currently not in any file, so the process should involve something like

cat "## Model answer\n\n```sql\n" query.sql "\n```" >> README.md

but cat only works with sets of files, not files and strings. How could I achieve the desired result in BASH?

sesodesa
  • 1,473
  • 2
  • 15
  • 24
  • 1
    You could make a compound statement `{ echo "Title"; cat file.sql; } > result.md` – Mark Setchell Aug 21 '19 at 09:57
  • @Mark Setchell This would remove the text already in the file `README.md`. – sesodesa Aug 21 '19 at 10:13
  • 1
    I don't fully understand what you are trying to do, so rather than making an answer, I just commented showing in broad terms, how to mix `echo` and `cat` in the hope you could work out the exact command you need. – Mark Setchell Aug 21 '19 at 10:16

2 Answers2

2

First you can printf or echo -e the required text and then cat the file query.sql after that. You can use && or ; between the commands . If && is used then the second command will be executed only if the first command was successfull. ; is treated as a command separator

{ printf "## Model answer\n\n \`\`\`sql" && cat query.sql && printf "\n\`\`\`"; } >> README.md

or

{ echo -e "## Model answer\n\n '''" ; cat query.sql ; echo -e "''' /n"; } >> README.md
j23
  • 3,139
  • 1
  • 6
  • 13
2

In bash (as you have tagged it) you can do

cat <(echo -e '## Model answer\n\n```sql') query.sql <(echo -e '```\n') >> README.md

The <() uses process substitution; the command in parentheses is run in a subshell and its output is inserted into the larger command line. This form allows you to run without stringing lots of commands together.

If you really want avoid running commands -- for speed's sake -- you can get fancier and do this:

cat <(echo -e '## Model answer\n\n```sql') query.sql - <<<'```'

This uses here strings instead of one command. The - means use standard input as an input to cat, and the <<<'```' at the end means send this string to standard input.

Finally the coup de grace. You can even do something like this; in your specific case it's a bit unwieldy but it demonstrates the concept:

$  cat /dev/fd/{4,5,6} query.sql /dev/fd/7 4<<<'### Model answer' 5<<<'' 6<<<'```sql' 7<<<'```'
### Model answer

```sql
Contents of query.sql
```

This means concatenate file descriptors 4, 5, and 6, query.sql, and file descriptor 7, while opening these file descriptors and sending the respective strings to them. (This doesn't require Linux [where /dev/fd was developed] to work, bash interprets /dev/fd itself and emulates it on systems that don't have it. This is why you can't do /dev/fd/[4-6].) I chose these file descriptors because 0-2 are reserved for stdin, stdout, and stderr, and 10 and above are reserved for bash, and I can't count correctly.

Vercingatorix
  • 1,838
  • 1
  • 13
  • 22