sesodesa
sesodesa

Reputation: 1603

How to concatenate a string and the contents of a file in BASH?

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?

Upvotes: 4

Views: 5356

Answers (3)

Rob Grant
Rob Grant

Reputation: 7348

You can read readme into a variable:

README=$(cat readme.md)

And the SQL file:

SQL=$(cat query.sql)

Combine them:

OUTPUT="$README

## Model answer

$SQL"

And echo that back to the file (double quotes needed for line breaks):

echo "$OUTPUT" > readme.md

Upvotes: 1

Vercingatorix
Vercingatorix

Reputation: 1884

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.

Upvotes: 2

j23
j23

Reputation: 3530

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

Upvotes: 3

Related Questions