0

Let's say I have a file called path.txt containing the text $HOME/filedump/ on a single line. How can I then read the contents of path.txt into a variable, while having Bash parse said content?

Here's an example of what I'm trying to do:

#!/bin/bash
targetfile="path.txt"

target=$( [[ -f $targetfile ]] && echo $( < $targetfile ) || echo "Not set" )
echo $target

Desired output: /home/joe/filedump/

Actual output: $HOME/filedump/

I've tried using cat in place of <, wrapping it in quotes, and more. Nothing seems to get me anywhere.

I'm sure I'm missing something obvious, and there's probably a simple builtin command. All I can find on Google is pages about reading variables from ini/config files or splitting one string into multiple variables.

Dave S
  • 788
  • 1
  • 11
  • 30

3 Answers3

2

If you want to evaluate the contents of path.txt and assign that to target, then use:

target=$(eval echo $(<path.txt))

for example:

$ target=$(eval echo $(<path.txt)); echo "$target"
/home/david/filedump/
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Aha! After your comment I'd tried about 10 different ways of running eval, but not this one. Just what the doctor ordered, thanks! – Dave S Feb 22 '16 at 06:14
  • In case anyone else is using the shorthand syntax in OP, I had to add an additional `echo`, resuling in ```target=$( [[ -f $targetfile ]] && echo $(eval echo $(<$targetfile)) || ... )```. – Dave S Feb 22 '16 at 06:21
  • 1
    Sure, glad I could help. The key with `eval` is understanding it will try and do what it is evaluating (e.g. it is as if you enter the result as a shell command). You don't want to execute the result, you want to assign the result to a variable. To assign the value, you want `eval` to output the result. `echo` causes `eval` evaluate the whole expression `echo $HOME/filedump/`. (which is something you *can* assign) – David C. Rankin Feb 22 '16 at 06:57
0

This might not necessarily suit your needs (depending on the context of the code you provided), but the following worked for me:

targetfile="path.txt"
target=$(cat $targetfile)
echo $target
mediantis
  • 145
  • 3
  • 14
  • This gets as far as grabbing the file content, but it doesn't actually convert `$PATH` to `/home/joe` as you'd expect. The echo will just show the raw text from path.txt. – Dave S Feb 22 '16 at 06:00
  • It personally converted into the filepath for me - not sure if something else in my setup (zsh?) is doing it already. A quick terminal-recording testing it out - https://asciinema.org/a/1o5gnn6k6v7phvz9cowtfmdgx Taking away the use of the bash script, cat path.txt by itself outputs the full path in both bash and zsh for me. I should probably test what output your code gives me too. – mediantis Feb 22 '16 at 06:01
  • Ah. It's something else - running the code you provided as-is prints the path for me. Are you using something else other than bash or zsh by any chance? – mediantis Feb 22 '16 at 06:06
  • I'm not, no. It's a bash script which I'm running from zsh on a new install of Arch, so there's almost zero 3rd party bits interfering. Problem solved by prepending an echo to David C's answer. The reason yours works is, in the video, you forgot to escape the `$` so the full path is already in `path.txt` - at least I think that's it. – Dave S Feb 22 '16 at 06:24
0

Here's a safer alternative than eval. In general, you should not be using configuration files that require bash to evaluate their contents; that just opens a security risk in your script. Instead, detect if there is something that requires evaluation, and handle it explicitly. For example,

IFS= read -r path < path.txt
if [[ $path =~ '$HOME' ]]; then
    target=$HOME/${path#\$HOME}
    # more generally, target=${path/\$HOME/$HOME}, but
    # when does $HOME ever appear in the *middle* of a path?
else
    target=$path
fi

This requires you to know ahead of time what variables might appear in path.txt, but that's a good thing. You should not be evaluating unknown code.

Note that you can use any placeholder instead of a variable in this case; %h/filedump can be detected and processed just as easily as $HOME/filedump, without the presumption that the contents can or should be evaluated as shell code.

chepner
  • 497,756
  • 71
  • 530
  • 681