-3

I have a file like this

> cat /tmp/lists
#       user0       db0
#       user1       db1
#       user2       db2

I want to extract two arrays (column 2, column 3) from this, so I end up with roles=(role0 role1 role2), dbs=(db0 db1 db2). Finally I want to go through those like this.

for i in "${!roles[@]}" ; do
  createdb "${dbs[$i]}" -O "${roles[$i]}"
done

which should create 3 dbs (db0, db1, db2) with it's respective owners (role0, role1, role2)

not I am certain this should work And I see that on one rhel9.6 machine, but on another rhel9.7 machine I fail with declaration of the arrays.

> dbs=($(grep ^# /tmp/lists | awk {'print $2'}))
-bash: eval: line 3: syntax error near unexpected token `('
-bash: eval: line 3: `echo dbs=($(grep ^# /tmp/lists | awk {'print $2'}))'

The declaration works, once I skip the enclosing () surrounding my grep command, like dbs=$(grep ^# /tmp/lists | awk {'print $2'})

But that does not provides a successful looping. I think because the arrays see all entries as one.

> for i in "${!dbs[@]}" ; do echo createdb ${dbs[$i]} -O ${roles[$i]} ; done
createdb db0 db1 db2 -O role0 role1 role2

Any idea, where the root cause may be lying? Maybe some issue with IFS ?

8
  • 2
    You show just one line of a multiline command. Show the complete command from the very beginning. Commented Jan 26 at 10:19
  • 1
    How is alpha, beta, gamma supposed to come out of that file? Commented Jan 26 at 11:54
  • 1
    Are we supposed to remove the user prefix from entries such as user0 to derive 0 per your roles=(0 1 2)? Commented Jan 26 at 12:11
  • 1
    Are you really saying that dbs=($(grep ^# /tmp/lists | awk {'print $2'}) works? That can't work, you have an opening parenthesis with no closing one. Please show us your actual code, these details matter. Commented Jan 26 at 13:36
  • 1
    -bash: eval: line 3: indicates at least 3 lines in your script and a call to eval ... which doesn't match with the single line of code you've provided (dbs=($(grep ^# /tmp/lists | awk {'print $2'}))); as others have suggested, update the question with your actual code; we should be able to cut-n-paste your data + code into our environment and generate the same errors/wrong-output as you're seeing, if we can't reproduce your issues then we're left guessing at what your real problem may be Commented Jan 26 at 14:40

3 Answers 3

3

If you have the information in a file, and just want to run commands for each line, you don't need to load everything to an array first.

Just loop over the input and run the commands:

while read -r dummy role db; do 
    echo createdb "$db" -O "$role"
done < inputfile

With the file shown, that prints

createdb db0 -O user0
createdb db1 -O user1
createdb db2 -O user2

and of course you'd remove the echo to actually run the commands.

If you need to mangle the data somehow in between, you can use the shell's parameter expansions. E.g. to change user0 to role0: role=role${role#user}.

Or you could preprocess the input file with grep or sed or whatever:

grep ^# inputfile | 
while read -r dummy role db; do 
    echo createdb "$db" -O "$role"
done

(or while ... done < <(grep ^# inputfile) in feature-rich shells)

0
2

Your code:

dbs=($(grep ^# /tmp/lists | awk {'print $2'}))

has a number of problems.

First and most important is that the single-quotes for the awk script are inside the { print $2 } curly-braces, when they should be wrapping the entire awk script, like awk '{print $2}'

Second is that if you're using awk, you really don't need to use grep.

Try something like this:

dbs=($(awk '/^#/ {print $2}' /tmp/lists))

or, using the bash built-in readarray AKA mapfile, like this:

readarray -t dbs <(awk '/^#/ {print $2}' /tmp/lists)

BTW, your /tmp/lists file has three white-space separated fields, not two:

  • $1: a # character
  • $2: user0, user1, user2
  • $3: db0, db1, db2

So you probably want to extract $3 for the dbs array. not $2.

0
2

Your code doesn't match your description. (The code shows what I assume is a role named user0 but your description refers to a role of 0. Similarly, your code shows a database named db0 but you mention alpha, beta, and gamma out of nowhere.) However, if we assume that the data file is correct and contains three fields:

  • literal hash mark (#)
  • role
  • database name

you can use a single line of awk to process the file and create your databases:

awk '/^#/ { cmd = sprintf("echo XX createdb '"'"'%s'"'"' -O '"'"'%s'"'"'\n", $3, $2); system(cmd) }' /tmp/lists

Output

XX createdb db0 -O user0
XX createdb db1 -O user1
XX createdb db2 -O user2

Remove echo XX when you're happy that the code will do what you expect.

I should probably mention the visually strange '"'"' structure. I want a single quote inside a single-quoted string. To do this I come out of the single quoted string, enter into a double-quoted string containing just a single quote, and then re-enter the single-quoted string. In long-hand it looks more like this, but with the strings abutted so the shell treats them as a single entity:

'first single-quoted' "'" 'more single-quoted' "'" 'last single-quoted'

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.