5

I have setup a Bash menu script that also requires user input. These inputs are wrote (appended to) a text file named var.txt like so:

input[0]='192.0.0.1'
input[1]='username'
input[2]='example.com'
input[3]='/home/newuser' 

Now what I am trying to accomplish is to be able to read from var.txt from a script kinda like this:

useradd var.txt/${input[1]}

now I know that wont work just using it for an example.

Thanks in Advance, Joe

codeforester
  • 39,467
  • 16
  • 112
  • 140
jmituzas
  • 593
  • 3
  • 14
  • 28

3 Answers3

16

Use bash's readarray statement. (It's the only way I can find to put spaces in array elements dynamically.) You'll need your var.txt file to simply contain the elements of the array, one on each line, not contain assignment statements.

readarray -t input < var.txt

For more info, try help readarray (which will then tell you to try help mapfile).

Here's my test for it:

echo -e "a\nb c\nd" > var.txt
readarray input < var.txt 
for item in "${input[@]}"; do echo $item; done

prints:

a
b c
d

Note: doing cat var.txt | readarray -t input doesn't work. I think it's because the input variable is scoped out of reach.

thejoshwolfe
  • 5,328
  • 3
  • 29
  • 21
  • Normally in a bash array spaces are considered separators and become distinct array elements. So the behavior when reading a file is not consistent? – Roland Apr 19 '21 at 12:16
  • In my previous comment I'm referring to the compound assignment name=(value1 value2 … ) – Roland Apr 19 '21 at 12:29
7

If the whole var.txt file contains only Bash-compatible variable assignments as you indicated, you might just be able to source it, to make those variables available in a new Bash script:

source var.txt

useradd ${input[1]}

This, however, will overwrite any existing variable with the same name. Command substitution can be used to avoid this, by selecting specific variables:

input[1]="$(grep '^input\[1\]=' var.txt | sed "s|[^=]*='\(.*\)'|\1|")"

It allows for renaming variables, although you will have to do this for each variable of interest. It essentially extracts the value of the variable from the var.txt file and assigns it to a new variable. See the grep manual page and the sed info page for more information on their use.

Process substitution may allow for simpler expressions:

source <(grep '^input\[[0-9]*\]=' var.txt)

useradd ${input[1]}

This would allow you to import only definitions of interest, although you have to watch for unwanted variable overwrites.

thkala
  • 84,049
  • 23
  • 157
  • 201
3

You can encapsulate your variable extraction in a function and take advantage of the fact that declare creates local variables when used inside a function. This technique reads the file each time the function is called.

readvar () {
    # call like this: readvar filename variable
    while read -r line
    do
        # you could do some validation here
        declare "$line"
    done < "$1"
    echo ${!2}
}

Given a file called "data" containing:

input[0]='192.0.0.1'
input[1]='username'
input[2]='example.com'
input[3]='/home/newuser'
foo=bar
bar=baz

You could do:

$ a=$(readvar data input[1])
$ echo "$a"
username
$ readvar data foo
bar

This will read an array and rename it:

readarray () {
    # call like this: readarray filename arrayname newname
    # newname may be omitted and will default to the existing name
    while read -r line
    do
        declare "$line"
    done < "$1"
    local d=$(declare -p $2)
    echo ${d/#declare -a $2/declare -a ${3:-$2}};
}

Examples:

$ eval $(readarray data input output)
$ echo ${output[2]}
example.com
$ echo ${output[0]}
192.0.0.1
$ eval $(readarray data input)
$ echo ${input[3]}
/home/newuser

Doing it this way, you would only need to make one call to the function and the entire array would be available instead of having to make individual queries.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • Wow! Thanks I have not had a chance to test these yet, but I am sure this will most definitely work for what I am trying to accomplish. Automated server installation and configuration-a must have in reproducing systems! Should have time over the weekend to test these out. Thanks to both of you! – jmituzas Jan 14 '11 at 18:42
  • 1
    @jmituzas: Note that in Bash 4 there is a builtin called "readarray" which is a synonym for "mapfile". These commands read lines from a file into an array. They don't do the variable setting as shown in my answer, but you could probably blend the the techniques. – Dennis Williamson Jan 21 '11 at 19:52
  • Awesome! All of these versions helped me out in so many ways. – jmituzas Mar 26 '11 at 01:46