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. You can check variables assignments format before sourcing :
#!/bin/bash
file=Settings.txt
regex_varname='^[a-zA-Z0-9_]\+\'
regex_varvalue='[a-zA-Z0-9]\+$'
is_safe_var() {
while read var; do
grep -q $regex_varname=$regex_varvalue <<< "$var" || return 1
done < "$file"
}
is_safe_var && source "$file" || echo "Break"
The variables $regex_varname
and $regex_varvalue
are assigned patterns for variables authorized name and value :
^[a-zA-Z0-9_]
: variable name beginning with one or more alphanumeric character or _
[a-zA-Z0-9]\\+$
: variable value ending with one or more alphanumeric character The loop in function is_safe_var
checks for each line of Settings.txt
if variables assignments match the pattern $regex_varname=$regex_varvalue
.
If one line fails the test, it returns from function with error code and echo "Break"
, otherwise Settings.txt
is sourced.
Note : you may complete the character range [a-zA-Z0-9_]
and [a-zA-Z0-9]
with authorized characters in your variables names and values.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.