Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash Getting variables from a file safely

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?


1 Answers

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).

    • If there are values that are single- or double-quoted and you want to remove the quotes, use the following printf -v command instead:
      printf -v "${BASH_REMATCH[1]}" %s "$(xargs -I {} printf %s {} <<<"${BASH_REMATCH[2]}")"
      but note that quoted strings with embedded, escaped quotes of the same type are not supported.
  • 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.

    • For simplicity, no attempt is made to validate the variable name up front, which means that the printf -v command may fail later.
like image 108
mklement0 Avatar answered Feb 19 '26 12:02

mklement0



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!