1

I have a text file which is usually filled with multiple lines which I want to "print" with a while loop. The text inside this file contains variables - my problem is, that these variables are not interpreted unlike a similar test-string containing variables stored inside the script. Is it possible to also interpret those variables from my external file or do I have to parse them beforehands etc.? What is the difference between $LINE_INSIDE and $LINE_OUTSIDE? I tried some suggestions from other questions like ${!varialbe_name} and different constructs with quote signs but with no luck so far.

    #!/bin/bash
    # color.sh
    BLUE='\033[1;34m'
    NC='\033[0m' # No Color

    LINE_INSIDE="${BLUE}Blue Text${NC}"
    echo -e ${LINE_INSIDE}

    while read LINE_OUTSIDE; do
            echo -e ${LINE_OUTSIDE}
    done < text_file

Output:

Output of the script

Additional Information: I (indeed) also have shell-commands in my input-text-file which should not by executed. Only the variables should be expaned.

n-tchen
  • 400
  • 3
  • 12
  • `eval "echo -e ${LINE_OUTSIDE}"` – Arkadiusz Drabczyk May 26 '19 at 17:59
  • A better comparison would be `LINE_INSIDE='${BLUE}Blue Text${NC}'` (single quotes). – muru May 26 '19 at 18:03
  • Please provide a sample set of lines of input data, illustrating variables that must be expanded and commands that must not. – roaima May 31 '19 at 19:59
  • First of all: My primary problem is solved with @Stéphane's answer. Secondly fyi: The input are shell-commands for look-up purposes (a cheat-list) with a caption/description, e.g.: `Mit Grep gefilterten Output an weiteren Befehl weiterleiten ls ${BLUE}/pfad${NC} | grep -e '${GREEN}Regular Expression${NC}' | xargs -I{} sudo mv {} ${BLUE}/pfad${NC} Löschen mit Fortschrittsanzeige sudo rm -rv ${BLUE}/pfad${NC} | pv -l -s $(sudo find ${BLUE}/pfad${NC} | pv -l | wc -l) > /dev/null` I should have mentioned it at the first place, so maybe I open a followup question regarding that. – n-tchen Jun 01 '19 at 07:46

2 Answers2

7

It would probably make more sense to write it as:

BLUE=$'\033[1;34m'
NC=$'\033[0m' # No Color

eval "cat << EOF
$(<text_file)
EOF
"

than using a while read loop (that's not the right syntax for reading lines btw).

Of course that means that code in there would be interpreted. A $(reboot) in there for instance would cause a reboot, but that's more or less what you're asking for.

That also assumes the text_file doesn't contain an EOF line.

Another approach that would only do variable (environment variable) substitution (and not command substitution for instance) would be to use GNU gettext's envsubst:

BLUE=$'\033[1;34m'
NC=$'\033[0m' # No Color
export BLUE NC
envsubst < text_file

Or so that only those two variables are expanded:

BLUE=$'\033[1;34m'
NC=$'\033[0m' # No Color
export BLUE NC
envsubst '$BLUE$NC' < text_file
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • I indeed have shell-commands in my text file, which should not be executed. I added this information to my original question. So the second part of your answer (export + envsub) (`while IFS= read LINE_OUTSIDE; do; echo -e ${LINE_OUTSIDE} | envsubst; done < text_file`) is what perfectly solves my problem. – n-tchen May 31 '19 at 16:55
  • @n-tchen that code of yours has a lot of problems, which could be eliminated by reducing it to simply `envsubst –  May 31 '19 at 18:01
  • @mosvy I guess I broke the problem too much down. In my actual script I do a couple of steps more (inside the loop) of "formatting" the text-file, so `envsubst – n-tchen May 31 '19 at 19:03
  • That's not about elegance. You're omitting the `-r` option of read and fail to properly quote your variables (which makes the `IFS=` quite pointless -- unless IFS was *really* changed in your script, its *only* purpose is to preserve the *trailing* spaces from the line, that were already lost in the unquoted `${...}`). And the `envsubst` is one extra fork + exec per line. But as you said, if the script is just for you, I guess that using `bash` is a form of penitence or masochism, so completely different standards may apply ;-) –  May 31 '19 at 19:45
  • Okay, I understand. The missing `-r` was just a typo (even though it's, in my case, also working without), but the quotes seems to be a point to think about. Next time my brain is capable enough I will go through your criticism, also regarding using bash. – n-tchen May 31 '19 at 20:06
3

In newer versions of bash (since version 4.4 or so) there's yet another tricky way to indirectly expand variables in strings, without having to go full eval: the @P parameter transformation (= expand as in prompts, including but not limited to variable substitutions).

$ cat file.txt
${BLUE}hello blue${NORM}

$ BLUE=$'\e[34m'
$ NORM=$'\e[m'
$ while IFS= read -r line; do printf '%s\n' "${line@P}"; done < file.txt
hello blue
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501