1

I have one project specific command which produces output in the below form:

Parameter1='value1' Parameter2='Value2' ... #Single quoted value for the variable.

But I wanted to explicitly assign the value and needed to print the parameters which has to show the respective values.

Here xtc_cmd get is the project specific cmd

root@renway:~# FOO=`xtc_cmd get lan_ifname lan_ipaddr lan_netmask`
root@renway:~#
root@renway:~# echo $FOO
SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'
root@renway:~#
root@renway:~# echo $SYSCFG_lan_ifname

root@renway:~# echo $SYSCFG_lan_ipaddr

root@renway:~# echo $SYSCFG_lan_netmask

However, I tried 'eval $FOO' after that the variables are printing their values. Due to security reasons, I wanted to skip 'eval'.

Sharing the output of script execution:

root@renway:~# /tmp/test.sh
++ xtc_cmd get lan_ifname lan_ipaddr lan_netmask
+ FOO='SYSCFG_lan_ifname='\''br1'\''
SYSCFG_lan_ipaddr='\''10.0.0.1'\''
SYSCFG_lan_netmask='\''255.255.255.0'\'''
+ echo 'SYSCFG_lan_ifname='\''br1'\''' 'SYSCFG_lan_ipaddr='\''10.0.0.1'\''' 'SYSCFG_lan_netmask='\''255.255.255.0'\'''
SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'

How to actually assign the values and print these variables.

Input string of interest

FOO='SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\'' SYSCFG_lan_pd_interfaces='\''brlan0 brlan19 brlan20'\'''

The expected output:

foo_SYSCFG_lan_ipaddr=10.0.0.1
foo_SYSCFG_sysdate='$(date>> /tmp/date.txt)0' #single quoted value
foo_SYSCFG_lan_pd_interfaces=brlan0 brlan19 brlan20 #whitespace separated string

The challenge here is SYSCFG_sysdate alone holds single quoted value '$(date>> /tmp/date.txt)0' compared to other params.

Sorry that I missed to highlight or mention this param at the earliest. This is to test malicious command injection attack. So the expectation here is the value to be stored as it is but without the command execution. With 'eval' builtin, date command is executing and which is not expected.

The desired output I got after running Zilog80's POSIX V1 script which uses 'set' builtin.

But POSIX V2 script is running fine only without SYSCFG_sysdate param.

Thanks especially to @Charles Duffy and @Zilog80 for their huge and valuable inputs and directions to this question.

  • As an aside -- get in the habit of using `echo "$FOO"` instead of `echo $FOO`; otherwise, one can run into the issues described in [I just assigned a variable, but `echo $variable` shows something else!](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else) – Charles Duffy Jun 14 '21 at 13:56
  • 2
    Please make sure you are avoiding `eval` because you don't trust xtc_cmd to output properly shell escaped words, and not because of "eval is evil" cargo culting. They are few and far between, but in some cases `eval` is the safe and robust choice. – that other guy Jun 14 '21 at 14:12
  • 1
    @thatotherguy, ...not entirely unsound advice, but if one is going to `eval` shell-escaped data, it's worth auditing how well that escaping is done. I've personally discovered two Java implementations of shell escaping that had exploitable faults -- one of them is PLXUTILS-161 / MSHARED-297, the other https://github.com/rundeck/rundeck/issues/298. I could see someone going "oh, the documentation says that's supposed to be escaped, it must be safe"; if it weren't for someone (who in those cases happened to be me!) checking the corner cases, nobody would have known. – Charles Duffy Jun 14 '21 at 15:13
  • (...or, if one had been unlucky, only the bad guys would have known; the market for selling vulnerabilities has only grown over time, so that's certainly a thing). – Charles Duffy Jun 14 '21 at 15:15

4 Answers4

6

Borrowing from an answer to a closely related question (Reading quoted/escaped arguments correctly from a string):

#!/usr/bin/env bash
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'"
 
case $BASH_VERSION in
  ''|[1-3].*) echo "ERROR: Bash 4.0 required; this is ${BASH_VERSION:-not bash}" >&2; exit 1;;
esac
 
declare -A kwargs=( )
while IFS= read -r -d ''; do
  [[ $REPLY = *=* ]] || {
    printf 'ERROR: Item %q is not in assignment form\n' "$REPLY" >&2
    continue
  }
  kwargs[${REPLY%%=*}]=${REPLY#*=}
done < <(xargs printf '%s\0' <<<"$FOO")
 
# show what we parsed for demonstration purposes
declare -p kwargs >&2

You can see this running in an online sandbox at https://ideone.com/KniaC4; its output is an associative array of the following form:

declare -A kwargs=([SYSCFG_lan_ifname]="br1" [SYSCFG_lan_netmask]="255.255.255.0" [SYSCFG_lan_ipaddr]="10.0.0.1" )

...so you can refer to "${kwargs[SYSCFG_lan_ifname]}", or "${kwargs[SYSCFG_lan_ipaddr]}".

This is more secure than assigning to regular bash variables because it doesn't let an attacker modify PATH, LD_PRELOAD, or other environment variables that modify behavior of the shell, linker, loader, standard C library, etc. (Note that even if you don't explicitly export the assignments created by this code, assigning to an already-exported variable will automatically export the new value; so security issues that only apply to environment variables and not regular shell variables can still be at play here).


Caveat: The way xargs parses strings is not quite compatible with the POSIX sh standard -- see the link given above for details and other options (Python has a fully-compliant parser, f/e, and the linked answer describes how to use it from bash).


Alternately, With Older Bash Releases

When associative arrays aren't available, one can prefix regular variables:

#!/usr/bin/env bash
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'"
 
while IFS= read -r -d ''; do
  [[ $REPLY = *=* ]] || {
    printf 'ERROR: Item %q is not in assignment form\n' "$REPLY" >&2
    continue
  }
  printf -v "foo_${REPLY%%=*}" '%s' "${REPLY#*=}"
done < <(xargs printf '%s\0' <<<"$FOO")
 
# show what we parsed for demonstration purposes

for var in ${!foo_*}; do
  echo "$var has value: ${!var}"
done

See this running at https://ideone.com/7UZJkT, with the output:

foo_SYSCFG_lan_ifname has value: br1
foo_SYSCFG_lan_ipaddr has value: 10.0.0.1
foo_SYSCFG_lan_netmask has value: 255.255.255.0
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thanks, your inputs are more valuable in terms of security vulnerabilities in using regular bash variables and PATH, LD_PRELOAD env variables. Unfortunately the bash version I have is 3.2.57(1)-release and so not able to use the associative array you suggested. – renga_in_stack Jun 15 '21 at 16:03
  • @renga_in_stack, see addendum showing a version that doesn't require associative arrays. – Charles Duffy Jun 15 '21 at 16:33
  • Thanks that the provided solution worked for me. Shall you please confirm whether the error can be ignored for below input string `FOO='SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\'''` – renga_in_stack Jun 16 '21 at 07:54
  • I understand that delimiter **-d ' '** in read is tokenizing the arguments. If I have a param-value as `SYSCFG_lan_pd_interfaces='\''brlan0 brlan1 brlan2'\''` then its failing for this case. Any help to make this case also to work. – renga_in_stack Jun 16 '21 at 12:36
  • @renga_in_stack, what do you consider "failing"? The inner quotes are literal; it's correct for them to be treated as data. If that's not here, it's the fault of xargs not being quite POSIX compliant in how it parses quotes, which is why the the linked question has a Python-based string splitting routine that _is_ POSIX-compliant in full. I talk about that in the answer already, and did even when it was first written. – Charles Duffy Jun 16 '21 at 13:48
  • @renga_in_stack, ...okay, I see what you're discussing, in the form of https://ideone.com/mCSKMw, the error looks to be correct. The space inside the `$(date >> /tmp/date.txt)` string is in an unquoted context (because the extra quotes you injected to simulate an attack close the quotes that were opened beforehand), so `xargs` is correct to splits out `$(date>>` as a separate string from `/tmp/date.txt)`, and the second half _isn't_ in fact an assignment. This all looks to be working as it should. – Charles Duffy Jun 17 '21 at 16:49
  • Yes, this date command is alone within '' single quote with whitespace and brings challenge in correctly parsing the single quoted value. I have edited the post which describes the actual input string FOO. Shall you please explain few words about the downside of using 'set' builtin. Because only with 'set', I got the expected output. – renga_in_stack Jun 18 '21 at 13:02
  • @renga_in_stack, first, to make sure we agree on definitions: What do you think correct output should be, and why? (If you count the quotes, there's an even number of them before the `date` command, so they cancel each other out, so it's incorrect to say that it's quoted; and since it isn't quoted, it's _correct_ to split on whitespace, which of why I'm defending behavior of the above code as proper). – Charles Duffy Jun 18 '21 at 13:28
  • I have no problems in the behavior of the code. But I don't know how to interpret the input string exactly because whatever I mentioned here is the output of project specific `xtc_cmd` which is stored in variable `FOO` with 'set -x' specified in the beginning of the script. I expect the output of the sysdate param to be `foo_SYSCFG_sysdate='$(date>> /tmp/date.txt)0'` – renga_in_stack Jun 18 '21 at 13:41
  • Command injection is done through below cmd `$ syscfg set sysdate "'\$(date>> /tmp/date.txt)0'"`. If we read `$ syscfg get sysdate`, then it displayed output as `'$(date>> /tmp/date.txt)0'`. So the variable `foo_SYSCFG_sysdate` has to show the same value as get cmd. – renga_in_stack Jun 18 '21 at 13:53
  • @renga_in_stack, ...the thing is, when you encapsulate that string _with outer quotes_, those outer quotes cancel out the inner ones. That is, `''$(date>> /tmp/date)0''` is identical to `$(date>> /tmp/date.txt)0` with no quotes at all; and when that's parsed as data without `$()` being special, it breaks into two words, `$(date>>` and `/tmp/date.txt)` – Charles Duffy Jun 18 '21 at 14:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/233933/discussion-between-charles-duffy-and-renga-in-stack). – Charles Duffy Jun 18 '21 at 14:18
3

To add to @CharlesDuffy answer, and for thoses who are still stuck with old 'unupgradable' hardware/vm, here is a POSIX / old bash way to achieve that in a safe manner. Tested with dash, ksh93, bash 2.05b, 3 and 4. Can't retrieve my old Bourne shell 92.

EDIT: Thanks to useful @CharlesDuffy comments :

  1. Updated to deal with blank/space/newline/wathever in the 'value' part. in a basic manner (multiple blanks reduced to one space, new lines swallowed). Work in progress for a better way to handle that.

  2. Produced variable names are now prefixed with _ to prevent any attempt to override PATH, LD_PRELOAD, etc.

EDIT2: Added a Bash 2/3/4 and ksh version that deals with tab/spaces/newline in values. See below.

EDIT3: Add a POSIX compliant rev 2 that can handle TAB, NEWLINE and multiple SPACE.

POSIX compliant V1 :

This one can't handle nicely newlines and tabs in the value part of the variable. It won't crash , but the related variables will be "compacted" in one line with spaces instead of newlines, and all tabs / multiple spaces are reduced to one space.

#!/bin/sh
# If you only have a bash 4.x, you can test with compat 3.1 bash
# shopt -s compat31
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' 
SYSCFG_lan_netmask='255.255.255.0' SYSCFG_space='my   space' SYSCFG_newline='I have 
many multi
lines input"
# An "env variable" definer that use the read command 
# to parse and define the env  variable
define() {
  IFS=\= read -r key value <<EOF
$1
EOF

  # Unquotting the value, adapt as it fit your needs
  value="${value#\'}"
  value="${value%\'}"
  read -r "_${key}" << EOF
${value}
EOF

}
unset _SYSCFG_lan_ifname
unset _SYSCFG_lan_ipaddr
unset _SYSCFG_lan_netmask
unset _SYSCFG_space
unset _SYSCFG_newline
# Using the set command to "parse" the variables string
set ${FOO}
while [ "$1" ] ; do
  key_value="$1"
  while [ "$1" ] && [ "${key_value%\'}" = "${key_value}" ]  ; do
    shift
    key_value="${key_value} $1"
  done
  define "${key_value}"
  [ "$1" ] && shift
done
echo "${_SYSCFG_lan_ifname}"
echo "${_SYSCFG_lan_ipaddr}"
echo "${_SYSCFG_lan_netmask}"
echo "${_SYSCFG_space}"
echo "${_SYSCFG_newline}"

The output is the same with ksh93, bash 2..4, dash :

br1
10.0.0.1
255.255.255.0
my space
I have many multi lines input

POSIX compliant V2 :

This version can handle special char and, partially, newlines. It does not use the set command to parse the string, avoiding any potential glob effect. We rely on the basic shell trimer # and %. This one can also handle different quoting in the string and escaped quotes/double quotes. The define function handles multi lines through \n in the here-doc, so their translation will be left to the script user.

#!/bin/sh
# If you only have a bash 4.x, you can test with compat 3.1 bash
# shopt -s compat31
# Test string. There is a TAB between "input" and "and".
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1 *' 
SYSCFG_lan_netmask=\"255.255.255.0\" SYSCFG_space='mypath\\ so\'urce\\my   space' 
SYSCFG_newline='I have 
many  multi 
 lines input    and /path/to/thi ngs'"
#
# Define here the prefix you want for the variables. A prefix is required
# to avoid LD_PRELOAD, PATH, etc. override from a variable.
_prefix="_"
# 
# The POSIX way for a new line constant.
_NL="
"
# An "env variable" definer that use the read command to parse and define 
define() {
  _key="$1"
  _value="$2"
  _quote="$3"
  _tmp=""
  # The POSIX read command can only read one line at a time.
  # For multiline, we loop to rebuild the full value.
  while read -r _line ; do 
    [ "${_tmp}" ] && _tmp="${_tmp}\n${_line}" || _tmp="${_line}";
  done  <<EOF
${_value}
EOF

  read -r "${_prefix}${_key}" << EOF
${_tmp}
EOF

}
unset _SYSCFG_lan_ifname
unset _SYSCFG_lan_ipaddr
unset _SYSCFG_lan_netmask
unset _SYSCFG_space
unset _SYSCFG_newline
# First, we trim blanks
_FOO="${FOO# * }"
_FOO="${_FOO% * }"
# We use shell basic trimer to "parse" the string
while [ "${_FOO}" ] ; do
  # Get the first assignation from the beginning
  _FOO_next="${_FOO#*=}"
  if [ "${_FOO_next}" != "${_FOO}" ] ; then
    # If there is backslash in the string we need to double escape them for 
    # using it as a pattern. We do that in a safe manner regarding FOO content.
    _FOO_next_pattern="$( sed 's/\\/\\\\/g' <<EOF
${_FOO_next}
EOF
    )"
    # We have an assignation to parse
    _key="${_FOO%=${_FOO_next_pattern}}"
    # We must have a key, assignation without key part are ignored.
    # If need, you can output error message in the else branch.
    if [ "${_key}" ] ; then
      # Triming space and newlines
      _key="${_key## }"
      _key="${_key##${_NL}}"
      _key="${_key## }"
      _quote="\'"
      # Test if it  is quote, if not quote then try double quote
      [ "${_FOO_next}" = "${_FOO_next#${_quote}}" ] && _quote="\""  
      # If not double quote, consider unquoted...
      [ "${_FOO_next}" = "${_FOO_next#${_quote}}" ] && _quote=""  
      # Extracting value part and trim quotes if any
      if [ "${_quote}" ] ; then 
        _FOO_next="${_FOO_next#${_quote}}"
        _FOO_next_pattern="${_FOO_next_pattern#${_quote}}"
      fi
      _value="${_FOO_next}"
      if [ "${_quote}" ] ; then 
        _FOO_next="${_FOO_next#*[^\\]${_quote}}"
        _FOO_next_pattern="${_FOO_next_pattern#*[^\\]${_quote}}"
      else
        # If the value part is not quoted, we look for the next unescaped space
        # as the delimiter for the next key/value pair.
        _FOO_next="${_FOO_next#*[^\\] }"
        _FOO_next_pattern="${_FOO_next_pattern#*[^\\] }"
      fi
      _value="${_value%${_quote}${_FOO_next_pattern}}"
      # We have parse everything need to set the variable
      define "${_key}" "${_value}" "${_quote}"
      _FOO="${_FOO_next}"
    else
      _FOO="${_FOO_next#*[^\\] }"
    fi
  else
    # Nothing more to parse
    _FOO=""
  fi
done
printf "%s\n" "${_SYSCFG_lan_ifname}"
printf "%s\n" "${_SYSCFG_lan_ipaddr}"
printf "%s\n" "${_SYSCFG_lan_netmask}"
printf "%s\n" "${_SYSCFG_space}"
printf "%s\n" "${_SYSCFG_newline}"

The output is the same with ksh93, bash 2..4, dash :

br1
10.0.0.1 *
255.255.255.0
mypath\ so\'urce\my   space
I have\nmany  multi\nlines input    and /path/to/thi ngs

BASH V2+ and KSH93 compliant :

It's non POSIX compliant since the variable substitution by pattern (/) is not POSIX. The literal ASCII inference $'\x<hex ASCII code>' is indeed not POSIX, and the following script can only work with ASCII based UNIX shells (forget EBCDIC...). Anyway, this one can handle newline/tab/multiple spaces in the value part of the variables.

#!/bin/sh
# If you only have a bash 4.x, you can test with compat 3.1 bash
# shopt -s compat31
# Test string. There is a TAB between "input" and "and".
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1 *' 
SYSCFG_lan_netmask='255.255.255.0' SYSCFG_space='mypath\\ source\\my   space' 
SYSCFG_newline='I have 
many  multi 
 lines input    and /path/to/thi ngs"
# 
# For bash 2.0, we can't make inline subsitution of ESC nor NL nor TAB  because
# of the following bug :
#  FOO="`echo -e \"multi\nline\"`";echo "${FOO//$'\x0a'/$'\x1b'}" ==> multi'line
# Bash 2.0 wrongly include one quote to the output in this case.
# To avoid that, we store ESC and NL in local variable, and it is better 
# for readability.
_ESC=$'\x1b'
_NL=$'\x0a'
_TAB=$'\x09'
# Same kind of trouble with the backslash in bash 2.0, the substiution need 
# 'double' escape for them in bash 2.0, so we store BKS, test it and double it 
# if required.
# However, if used as a variable in pattern or subsitution part, we have then to
# deal with two forms of escaped bakcslash since shells don't "dedouble"/escape
# them  for the substitute value, only for the pattern.
_BKS_PATTERN="\\\\"
_BKS="\\"
if [ "${_BKS_PATTERN//\\/X}" != "XX" ] ; then
  # Hello bash 2.0
  _BKS_PATTERN="\\\\\\\\"
  _BKS="\\\\"
fi
# An "env variable" definer that use the read command to parse and define 
define() {
  IFS=\= read -r _key _value <<EOF
$1
EOF

  # Unquotting the _value, adapt as it fit your needs
  _value="${_value#\'}"
  _value="${_value%\'}"
  _value="${_value%\'${_BKS_PATTERN}}"
  # Unescape the _key string to trim escaped nl
  _key="${_key#${_ESC}}"
  _key="${_key%${_ESC}}"
  # Unescape the _value string
  _value="${_value//${_BKS_PATTERN} / }"
  _value="${_value//${_ESC}${_ESC}/${_TAB}}"
  _value="${_value//${_ESC}/${_NL}}"
  read -d\' -r "_${_key}" <<EOF
${_value}'
EOF

}
unset _SYSCFG_lan_ifname
unset _SYSCFG_lan_ipaddr
unset _SYSCFG_lan_netmask
unset _SYSCFG_space
unset _SYSCFG_newline
# First, we escape the new line with 0x1B
_FOO="${FOO//${_NL}/${_ESC}}"
# Second, escape each tab with double ESC. All tabs.
_FOO="${_FOO//${_TAB}/${_ESC}${_ESC}}"
# Third, escape each space. All space.
_FOO="${_FOO// /${_BKS} }"
# Using the set command to "parse" the variables string
set ${_FOO}
while [ "$1" ] ; do
  _key_value="$1"
  while [ "$1" ] && [ "${_key_value%\'${_BKS_PATTERN}}" = "${_key_value}" ] ; do
    shift
    _key_value="${_key_value} $1"
  done
  define "${_key_value}"
  [ "$1" ] && shift
done
printf "%s\n" "${_SYSCFG_lan_ifname}"
printf "%s\n" "${_SYSCFG_lan_ipaddr}"
printf "%s\n" "${_SYSCFG_lan_netmask}"
printf "%s\n" "${_SYSCFG_space}"
printf "%s\n" "${_SYSCFG_newline}"

The output is the same with ksh93, bash 2 and + :

(Note that we use printf to render the TAb char between "input" and "and".)

br1
10.0.0.1 *
255.255.255.0
mypath\ source\my   space
I have
many  multi
 lines input    and /path/to/thi ngs
Zilog80
  • 2,534
  • 2
  • 15
  • 20
  • 1
    BTW, one way to get the same safety my answer gets from putting everything in a `kwargs` associative array with a POSIX-y answer would be to prefix the variables -- so, say, `read "foo_$key"` to prefix everything with `foo_`, so a variable like `PATH` or `LD_PRELOAD` can't be overridden. – Charles Duffy Jun 14 '21 at 18:54
  • ...btw, I'm not quite sure that `set` does what you want it to -- it doesn't honor quotes as-is. Let me build an example in a sandbox to demonstrate with slightly modified data... – Charles Duffy Jun 14 '21 at 18:56
  • 1
    See https://ideone.com/5DWuwS for the above-promised example of how this fails with `var_with_two_words='foo bar'` – Charles Duffy Jun 14 '21 at 18:59
  • @CharlesDuffy Nice catch, i 've focused on the question string without considering blank/space/newline. Basically, `read` should not work also with `$` and ` ` ` in the variable name nor value (expected for varname, but not for the value). Prefixing the var name, good idea to prevent LD_PRELOAD ^^ – Zilog80 Jun 14 '21 at 19:42
  • @CharlesDuffy I've edited to include a basic way to handle blank/space/new lines. It could be better, work in progress. And indeed include the variable prefix to prevent PATH/LD_PRELOAD/etc. hacks (thanks). Still POSIX, but i guess we'll have to drop `set` at some point to handle _complex_ variable values properly... – Zilog80 Jun 14 '21 at 20:40
  • 1
    I'll wait for you to finish out robust handling for surprising values before giving a +1, but this all looks promising. BTW, consider including `'value with * a glob'` in your test set (and making sure the `*` doesn't get replaced with a list of filenames when the code is executed in a non-empty directory). – Charles Duffy Jun 14 '21 at 20:55
  • @CharlesDuffy Added a "yet non POSIX" version which works with Bash 2.0 and Ksh93, handling newlines and TABs (but no more EBCDIC based shells...) and preserving `set` usage for safety. I look to make a pure POSIX one (will not be easy, as we can't manipulate char codes). – Zilog80 Jun 15 '21 at 16:00
  • I'm honestly not quite as comfortable as I'd like to be that `set` always does the right thing, though I don't have the time to try to break it right now. – Charles Duffy Jun 15 '21 at 18:17
  • @CharlesDuffy I've added a POSIX version 2 that can handle TAB, escaped quotes, etc. and can _somehow_ handle multiline. That without using `set` for your concern. For now, i have a way to have POSIX proper multiline definitions (using subverted redirections), but that requires `/proc` fs. Maybe one day i will find a way to trick the POSIX `read` command... – Zilog80 Jun 16 '21 at 17:23
  • @Zilog80 - Thanks that your POSIX V1 script really worked for me. But as Charles pointed, it uses 'set' builtin. With POSIX V2 script, I got processing issue due to single quoted value stored in SYSCFG_sysdate param. – renga_in_stack Jun 18 '21 at 13:07
  • @renga_in_stack We'll debug ^^. The single quote in SYSCFG_sysdate is an escaped one ? Could you provide the SYSCFG_sysdate string part ? – Zilog80 Jun 18 '21 at 13:10
  • @Zilog80 - `SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\''` . If you want the whole FOO string, you can get it from the question section. – renga_in_stack Jun 18 '21 at 13:13
  • @renga_in_stack The actual POSIX V2 can't deal with unescaped quote in the value part. In that case, only a double quoted `SYSCFG_sysdate="\'''\''\$(date>> /tmp/date.txt)0'\'''\''"` can be parsed. But that can be supported, to the condition that the unescaped single quotes are equally paired. That's mean a more complex POSIX script (check for the case + switch automatically the value part to double quoting), are you ok with that ? – Zilog80 Jun 18 '21 at 14:17
0

How to actually assign the values and print these variables.

Write a parser and parse the input and assign the variables. Read a line, split the line on first =, the left part is variable the right part is variable value, remove ' quotes or parse quotation style in whatever style does the command output and assign the result to the variable name extracted from the line. Along in pseudocode:

while <split line on first =>; do
    vareiable_value=$(parse second part after =)
    print -v "$variable_name" %s "$variable_value"
done <<<"$FOO"
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    Caveat is that while this doesn't use `eval`, it still does have security impact; an attacker who has only limited access, like the ability to write to `/tmp`, can set `PATH=/tmp/evil:/bin:/usr/bin:/usr/local/bin`, and thus cause an executable running with these privileges to run executables of their choice. Not the only one that constitutes a risk -- see `LD_PRELOAD`, etc. – Charles Duffy Jun 14 '21 at 13:43
0

Assuming you already know the names of the variables (or have a means of finding them), and you don't have to worry about security issues (see Charles Duffy's answer), you could source from a string:

$ FOO='SYSCFG_lan_ifname='\''br1'\'' SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_lan_netmask='\''255.255.255.0'\'''

$ set | grep SYSCFG
FOO='SYSCFG_lan_ifname='\''br1'\'' SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_lan_netmask='\''255.255.255.0'\'''

$ . <(echo "${FOO}")

$ set | grep SYSCFG
FOO='SYSCFG_lan_ifname='\''br1'\'' SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_lan_netmask='\''255.255.255.0'\'''
SYSCFG_lan_ifname=br1
SYSCFG_lan_ipaddr=10.0.0.1
SYSCFG_lan_netmask=255.255.255.0
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
  • 1
    source is making the command executable if I have FOO like below `FOO='SYSCFG_lan_ifname='\''br1'\'' SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_lan_netmask='\''255.255.255.0'\'' SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\'''`. The main concern for me is command should not be executed. – renga_in_stack Jun 16 '21 at 06:34