I have a script that stores variables inside a .txt file for later use. I want to retrieve those variables from the file safely.
How I have it setup now:
Settings.txt
var1=value1
var2=value2
...
Script
for i in $(cat Settings.txt); do $i done
# So now "var1" has the value "value1", and so on
This works, but is dangerous, as someone could inject code trough that txt file.
I know about the source command, but that too has the same problem. So, how to achieve the same functionality safely?
If you don't want separate steps for validation and variable creation:
Update, based on declare: A simpler approach that is still safe is to use the declare builtin to define the variables:
#!/usr/bin/env bash
# Read the file line by line.
while read -r line; do
declare -- "$line"
done <<'EOF'
var1=value1
var2=value2
EOF
The declare command fails with input lines that aren't valid shell variable assignments, but it fails safely in that the line is never evaluated as a command.
Note that the values are read as literals, exactly as defined in the file (except for removal of trailing whitespace).
If you also want to support single- or double-quoted values, use the following declare command instead:
declare -- "$(xargs -I {} printf %s {} <<<"$line")"
but note that using embedded, escaped quotes of the same type in values is not supported (this is a limitation of xargs).
Original answer, based on printf -v:
#!/usr/bin/env bash
# Read the file line by line.
while read -r line; do
# Split the line into name and value using regex matching.
if [[ $line =~ ^([^=]+)=(.*)$ ]]; then
# ${BASH_REMATCH[1]} now contains the variable name,
# ${BASH_REMATCH[2]} the value.
# Use printf -v to store the value in a variable.
printf -v "${BASH_REMATCH[1]}" %s "${BASH_REMATCH[2]}"
fi
done <<'EOF'
var1=value1
var2=value2
EOF
# Print the variables that were created (based on name prefix `var`).
for name in ${!var*}; do
printf '%s=%s\n' "$name" "${!name}"
done
Note that the values are read as literals, exactly as defined in the file (except for removal of trailing whitespace).
printf -v command instead:printf -v "${BASH_REMATCH[1]}" %s "$(xargs -I {} printf %s {} <<<"${BASH_REMATCH[2]}")"Should be safe to use, because printf -v is used to create the variables - the shell doesn't directly source the assignment statements, which is where injection could happen.
Lines not recognized as variable assignments are simply skipped.
Regex ^([^=]+)=(.*)$ matches any line that starts with (^) a least 1 (+) character other than = ([^=]), followed directly by =, followed by any remaining sequence of characters (.*) through the end of the line ($). The parentheses around ([^=]+) and (.*) ensure that the captured substrings are saved in special Bash array variable ${BASH_REMATCH[@]}, starting at index 1.
printf -v command may fail later.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With