0

This is a simplified version of the problem I'm experiencing just for demonstration purposes. But the command output when it contains a variable reference doesn't seem to go through an evaluation process to populate the variable reference it contains.

So do demonstrate I create a txt file (/mnt/external.txt) with the following line of text "${var1}/filename.txt". I then do a bash script like the following:

#!/bin/bash
var1="/home/user1"
echo $(cat /mnt/external.txt)

This then outputs "${var1}/filename.txt" rather than "/home/user1/filename.txt".

Is there a way to get it to re-evaluate the output of the cat command (just used cat to demonstrate the problem) to have it populate the variable reference with the variable value instead?

Aklys
  • 461
  • 1
  • 4
  • 15
  • If these are environment variables (i.e. exported), you could pipe it through `envsubst`. – Gordon Davisson Dec 09 '22 at 01:20
  • I don't want to really take the variables outside of the script if possible. – Aklys Dec 09 '22 at 01:21
  • I'm not sure what you mean by taking the variables "outside of the script". Exporting a variable only makes it available to subprocesses of that shell, not anything outside of it. If you know which variables are relevant, you could also put them specifically in the environment of the `envsubst` process with something like `... | var1="$var1" var2="$var2" envsubst`. – Gordon Davisson Dec 09 '22 at 01:24
  • Sorry I get what you mean. But can I use envsubst on the output of a command? As this is just a simplified example of the problem I have, the original problem is a command that outputs different text each time it is run in a loop. Can I pipe the command output into envsubst or does it only work when consuming a text file? The whole aspect of my issue as per the subject line is working with the output of a command that contains a reference to a variable. – Aklys Dec 09 '22 at 01:29
  • @Aklys: very very few Unix utilities care where their input comes from. The whole point of the "everything-is-a-file" philosophy that underlies Unix tools is that you never have to ask that question. – rici Dec 09 '22 at 01:45
  • Yeah except when I feed the command output to envsubst it tells me it can't find the file – Aklys Dec 09 '22 at 01:59
  • ok I got envsubst to work with piping over the output. Thanks for the help everyone. – Aklys Dec 09 '22 at 02:21

4 Answers4

2

You can invoke envsubst for all of the output of your command, which is something you mentioned in your comments but not your question.

#! /bin/bash

export MYVAR=myval

for i in {0..3}
do
    echo "$i: \$MYVAR"
done | envsubst

and the output is

0: myval
1: myval
2: myval
3: myval
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • Worth noting that `envsubst` is an external command, available in the GNU gettext package. – dan Dec 09 '22 at 04:08
1

This prints what you want, but I agree with Gordon that it's not safe.

#!/usr/bin/env bash

export var1="/home/user1"
echo "$(eval "echo $(< /mnt/external.txt)")"
John
  • 163
  • 9
0

You need to pass the returned string thru eval for the substitution to take effect. Your snippet should be modified as follows:

#!/bin/bash
var1="/home/user1"
eval echo $(cat /mnt/external.txt)
Eric Marceau
  • 1,601
  • 1
  • 8
  • 11
  • Thank I thought it would be something simple like that. But when I try apply that to my original more complex problem. I end up with errors like "syntax error near unexpected token `(' " in reference to a line that is "eval echo ${ARG__IMPORT}" – Aklys Dec 09 '22 at 01:55
  • 1
    This isn't very safe, because `eval` interprets *every bit of shell syntax* in the output, not just variables (that's what'll be causing the error with `(` in the string). You can improve it a bit with double-quoting (and you need *two layers* of double-quoting), but there's still a lot of things that can go wrong, depending on what's in the output. – Gordon Davisson Dec 09 '22 at 02:25
  • yeah I found this was part of my issue I think @GordonDavisson thanks again for your direction. – Aklys Dec 09 '22 at 02:26
0

You can use a heredoc to expand variables and command substitutions only:

eval "
cat <<EOF
$(</mnt/external.txt)
EOF"

Here eval is used only to print the file external.txt, so the heredoc can expand it.

This is safer and more predictable than eval "echo '$(<external.txt)'" (even with quotes). But still open to code injection if external.txt contained a command sub like $(rm important.txt), or the heredoc delimiter:

EOF
rm important.txt

The difference is that it's slightly more predictable than eval + echo, where stray quotes and/or list operators (; | & etc) could cause code execution.

dan
  • 4,846
  • 6
  • 15