On Wed, Mar 20, 2024 at 07:11:34AM -0400, Zachary Santer wrote: > On Wed, Mar 20, 2024 at 12:29 AM Lawrence Velázquez <v...@larryv.me> wrote: > > A couple of previous discussions: > > - https://lists.gnu.org/archive/html/bug-bash/2020-12/msg00066.html > > - https://lists.gnu.org/archive/html/bug-bash/2023-06/msg00128.html > > There I go, reporting a bug that isn't a bug again. > > One would think that enabling this behavior would be the entire > purpose of the alternate ( key value ) syntax. If it doesn't do that, > what benefit does it give over the standard ( [key]=value ) syntax? > Maybe it;s easier to use eval with?
I believe the "X" in this X-Y problem is "I want to serialize an associative array to a string, send the string to another bash script, and de-serialize it back into an associative array there." So, the first thing we observe is the behavior of the @k and @K expansions: hobbit:~$ declare -A aa=('key 1' 'value 1' 'key 2' 'value 2') hobbit:~$ declare -p aa declare -A aa=(["key 2"]="value 2" ["key 1"]="value 1" ) hobbit:~$ printf '<%s> ' "${aa[@]@k}"; echo <key 2> <value 2> <key 1> <value 1> hobbit:~$ printf '<%s> ' "${aa[@]@K}"; echo <"key 2" "value 2" "key 1" "value 1" > Essentially, @k serializes the associative array to a list of alternating keys/values, while @K serializes it to a string. For the purposes of sending the serialization to another script, the list is not suitable unless we NUL-terminate each element. We could pursue that, but it's going to involve more work, I suspect. Given the string serialization we get from @K, it makes sense to start there, and try to determine whether it's eval-safe. hobbit:~$ declare -A mess=('*' star '?' qmark $'\n' nl '$(date)' cmdsub '`id`' backticks) hobbit:~$ declare -p mess declare -A mess=([$'\n']="nl" ["?"]="qmark" ["*"]="star" ["\$(date)"]="cmdsub" ["\`id\`"]="backticks" ) That's not comprehensive, but it's a start. hobbit:~$ printf '<%s> ' "${mess[@]@K}"; echo <$'\n' "nl" "?" "qmark" "*" "star" "\$(date)" "cmdsub" "\`id\`" "backticks" > hobbit:~$ serial="${mess[@]@K}" hobbit:~$ printf '<%s>\n' "$serial" <$'\n' "nl" "?" "qmark" "*" "star" "\$(date)" "cmdsub" "\`id\`" "backticks" > There's our serialization to a string. Now we'll try to de-serialize: hobbit:~$ eval "declare -A copy=($serial)" hobbit:~$ declare -p copy declare -A copy=([$'\n']="nl" ["?"]="qmark" ["*"]="star" ["\$(date)"]="cmdsub" ["\`id\`"]="backticks" ) Looks OK. Let's verify another way: hobbit:~$ for i in "${!copy[@]}"; do printf '<%s>: <%s>\n' "$i" "${copy[$i]}"; done < >: <nl> <?>: <qmark> <*>: <star> <$(date)>: <cmdsub> <`id`>: <backticks> Unless someone else comes up with a key or value that breaks the eval, this looks like the simplest way. By comparison, the NUL-terminated @k list can't even be stored in a variable, so you'd need to transmit it to the other script in some way that's equivalent to using a temp file. Thus: hobbit:~$ printf '%s\0' "${mess[@]@k}" > tmpfile hobbit:~$ unset -v copy; declare -A copy; while IFS= read -rd '' key && IFS= read -rd '' value; do copy[$key]=$value; done < tmpfile hobbit:~$ declare -p copy declare -A copy=([$'\n']="nl" ["?"]="qmark" ["*"]="star" ["\$(date)"]="cmdsub" ["\`id\`"]="backticks" ) I can't think of any way to restore an @k serialization other than a IFS= read -rd '' loop. The only advantage I can see here is that it doesn't use eval, and therefore is visibly safe. With the eval @K solution, we're still left wondering whether the script is a ticking time bomb.