0

Having credentials file like this one:

db: db_name
user: username
password: password

After this command has been run:

awk '$1 ~ /db:|user:|password:/ { print $2 }' credentials \
| while read line; do c+=($line); done;

, I won't have a possibility to echo out my ${c[@]} array, because pipe process has terminated, and variable is gone. How would I manage to save that after while loop is terminated, so that I could use elements of that c array, as credentials for example in following command:

mysql -u ${c[1]} -p${c[2]} ${c[0]} < file.sql
branquito
  • 3,864
  • 5
  • 35
  • 60

5 Answers5

3

Don't use pipe since it creates a sub process and all the variable created under it will not be available in parent shell. Use process substitution instead:

while read -r line; do 
    c+=($line)
done < <(awk '$1 ~ /db:|user:|password:/ { print $2 }' credentials)
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Thank you, given that yours solution is pretty similiar to that of @Blue Moon, I marked his answer as accepted, because he was few seconds faster poster :) – branquito Oct 16 '14 at 18:28
  • 1
    Difference is [command substitution vs process substitution](http://stackoverflow.com/questions/15416570/command-substitution-vs-process-substitution) – anubhava Oct 16 '14 at 18:33
  • I know that, you have `fd` in place of `<(...)`, and bellow is command that executes giving line by line as `herestring` to while read, is your method faster, or better in some particular way? – branquito Oct 16 '14 at 18:35
  • 1
    [Yes as per this Q&A process substitution seems to be substantially faster](http://unix.stackexchange.com/questions/127645/performance-differences-between-pipelines-and-process-substitution) – anubhava Oct 16 '14 at 18:39
  • 1
    Oh I didn't intend to make you change your decision, it was just an academic discussion. – anubhava Oct 16 '14 at 18:46
3

You don't need a loop at all:

$ c=( $( awk '$1 ~ /db:|user:|password:/ { print $2 }' file ) )
$ echo "${c[0]}"
db_name
$ echo "${c[1]}"
username
$ echo "${c[2]}"
password
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
2

Instead of redirecting it to pipe you could do:

while read line; do 
  c+=($line)
done <<<"$(awk '$1 ~ /db:|user:|password:/ { print $2 }' credentials)"

Now the array c will be available even after the loop exited.

However, you could directly read from the file credentials instead of using awk as you seem to be using awk only for splitting the line, which you could in bash itself:

while read line; do 
  line=${line##*:}
  c+=($line)
done <credentials
P.P
  • 117,907
  • 20
  • 175
  • 238
1

You could just do the whole thing in awk:

$ awk -F ': ' '{a[$1]=$2}END{system("echo mysql -u " a["user"] " -p" a["password"] " " a["db"] " < file.sql")}' file
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • Yes, I thought of that too, after posting here my question. Good solution, to use END block and system call – branquito Oct 16 '14 at 18:17
  • But you trimmed of my conditional statement, that `credentials` file holds much more than those three lines ;) Oh I see now, it will still work, because of associative array, sorry. – branquito Oct 16 '14 at 18:18
  • Extra lines in the file shouldn't cause any problem. To be honest, I don't think that there's much advantage to any of these approaches over simply parsing the file into variables. – Tom Fenech Oct 16 '14 at 18:23
  • Sorry I don't understand. What do you mean by that? – branquito Oct 16 '14 at 18:26
  • I meant `db=$(awk '/^db:/{print $2}' file)`, etc. – Tom Fenech Oct 16 '14 at 18:28
  • Oh yes, if I would use a script like solution, but I wanted one-liner, because I will run that often as bash alias. – branquito Oct 16 '14 at 18:32
0

credentials.txt (text file):

db: db_name
user: username
password: password

You can just assign the variables in awk- then after processing all rows in the the credentials file (or stdin), print out the command you want to run and pipe it to the shell to execute it.

cat credentials.txt|awk '/db/{db=$2} /username/{username=$2} /password/{password=$2} \
END{print "mysql -u "username" -p "password" "db" < file.sql"}' |sh

That will print: mysql -u username -p password db_name < file.sql

Then pass it to the shell and execute the query.