help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: looping over variables & exporting them at the same time ?


From: Greg Wooledge
Subject: Re: looping over variables & exporting them at the same time ?
Date: Sat, 27 Jan 2024 10:11:51 -0500

On Sat, Jan 27, 2024 at 08:22:17AM -0500, heavy.gear5130@fastmail.com wrote:
> I have two variables defined in my env.
> 
>       echo $A; echo $B
> 
>               this is a test xtest of A str
>               this is a test xtest of B str

The indentation is confusing here.  Is there a bunch of leading whitespace
in each variable's contents?  Or did you simply indent the output?

Also, you said "in my env".  Are these really environment variables, or
are they just regular variables?  (It might not matter.)

> I want to loop over the varable names, A & B, editing the strings and 
> exporting the values.

This is quite unclear.  You want to loop over the *names*?  What does
that mean?  What does "edit" mean?  What "values" are you talking about?

> One at a time, without a loop, I would do
> 
>       export A=$( echo "$A" | sed -e 's| xtest | |g' )
>       export B=$( echo "$B" | sed -e 's| xtest | |g' )

Are the contents actually *lists*?  Lists of words?  With whitespace
separating them?

And you'd like to *remove* one word from a given list, resulting in a
new, smaller list?

(Also by the way, what you've got here doesn't work if the word to be
removed is at the beginning or end of the string.)

> So after,
> 
>       echo $A; echo $B
> 
>               this is a test of A str
>               this is a test of B str

It seems so.  Although you're doing it in the most *primitive* way
imaginable.

>       for vars in A B
>       do
>         export $vars=$( echo $vars | sed -e 's| xtest | |g' )
>       done

> I guess I need some sort of indirection or escaping ?

Yikes.  This is a mess.

The *first* thing I'm going to say is that you are working in the wrong
language.  Bash doesn't have the type of data structures you're trying
to mock up here.  What you actually want is a *dictionary of lists of
strings*.

In most other languages, you could just specify that.  "I want a
dictionary, indexed by strings A B etc., whose values are lists
of words."  Then you'd just write code to manipulate those lists.

Bash has dictionaries (associative arrays), and it has lists, kind of
(indexed arrays).  But what it does *not* have are dictionaries *of*
lists.  It only has dictionaries of strings.

So to do this in bash, you'd need to mock up at least one layer.  In
this case, you'd most likely store your list in a string variable,
exactly as you're doing now.  Then you could have a dictionary of
strings, and you could de-serialize and re-serialize each string back
and forth between string form and list form as needed.  (Hopefully
without calling *sed* of all things.)

But then you've got this other "oh by the way" piece in your puzzle:
you want each dictionary index value to be a *valid environment
variable name*, and you want to *export* each piece of the dictionary
to the environment.  For some completely unknown reason.  That's
the part that's going to be especially ugly.

And you never really specified where these lists come from.  It's
implied that they arrive in the form of environment variables.  (This
is horrifying, by the way.  It reminds me of sysadmin crap that you'd
see in stuff that was written as if we were still in the 1980s.)

So, let's assume the following:

 1) Your inputs are environment variables.
 2) Your outputs are also environment variables.
 3) The output variable name is the same as the input variable name.
 4) You KNOW IN ADVANCE the names of each environment variable that
    you will process.  You do NOT need to scan through the entire
    environment looking for matching patterns in the variable names.
 5) Each variable contains a list of words delimited by spaces.
 6) No word contains spaces internally.  There is no quoting needed.
 7) The processing to be performed is the removal of ONE word from EACH
    variable's contents, if found.  All instances should be removed.
 8) You are working in bash, not sh.
 9) The input variable contents do not contain newline characters.

Given the primitive nature of the task, we'll use primitive techniques.
No dictionary is needed, because we will only process one variable at
a time.

Here's a script based on what it appears you were trying to do:

#!/bin/bash
# This should work in bash 3.1 or newer.

vars=(A B)
remove=xtest

for v in "${vars[@]}"; do
    # Skip missing variables.
    if [[ ! ${!v+defined} ]]; then
        printf 1>&2 '%s is not defined. Skipping.\n' "$v"
        continue
    fi

    # De-serialize the string into a list.
    # This part will fail if the contents may contain newlines.
    IFS=' ' read -ra list <<< "${!v}"

    # Scan the list for the word to be removed, generating a new list.
    outlist=()
    for word in "${list[@]}"; do
        [[ $word != $remove ]] && outlist+=("$word")
    done

    # The previous step could also have been done "in-place" by
    # iterating the list by index, and unsetting the array values
    # that need to be removed.  Generating a second list is simpler.
    # Feel free to use the unset method if you prefer that.

    # Re-serialize the output list back to a string.
    # This uses the value of IFS, which we're assuming is default.
    # If your script changes IFS, then this line will need to change.
    out="${outlist[*]}"

    # Re-export it under its original name.
    # Post-eval, we will issue this command: export var="$out"
    # This is safe ONLY because we control the contents of v.
    # If v could be found by a dynamic scan of the incoming environment
    # variables, we would need an additional sanity check on the name.
    eval "export $v=\"\$out\""
done



reply via email to

[Prev in Thread] Current Thread [Next in Thread]