9

I would like to control a bash script like this:

#!/bin/sh
USER1=_parsefromfile_
HOST1=_parsefromfile_
PW1=_parsefromfile_
USER2=_parsefromfile_
HOST2=_parsefromfile_
PW2=_parsefromfile_

imapsync \
--buffersize 8192000 --nosyncacls --subscribe --syncinternaldates --IgnoreSizeErrors \
--host1 $HOST1 --user1 $USER1 --password1 $PW1 --ssl1 --port1 993 --noauthmd5 \
--host2 $HOST2 --user2 $USER2 --password2 $PW2 --ssl2 --port2 993 --noauthmd5 --allowsizemismatch

with parameters from a control file like this:

host1 user1 password1 host2 user2 password2
anotherhost1 anotheruser1 anotherpassword1 anotherhost2 anotheruser2 anotherpassword2 

where each line represents one run of the script with the parameters extracted and made into variables.

what would be the most elegant way of doing this?

PAT

Napster_X
  • 3,373
  • 18
  • 20
perler
  • 531
  • 2
  • 6
  • 10

4 Answers4

11

With shell scripts, this is normally accomplished using the source function, which executes the file as a shell script as if it was inlined into the script you're running -- which means that any variables you set in the file are exported into your script.

The downside is that (a) your config file is executed, so it's a security risk if unprivileged users can edit privileged config files. And (b) your config file syntax is restricted to valid bash syntax. Still, it's REALLY convenient.

config.conf

USER=joe
PASS=hello
SERVER=127.0.0.2

script.sh

#!/bin/bash

# Set defaults   
USER=`whoami`

# Load config values
source config.conf

foobar2000 --user=$USER --pass=$PASS --HOST=$HOST

source can be abbreviated with a single . -- so the following two are equivalent:

 source file.sh
 . file.sh
tylerl
  • 15,055
  • 7
  • 51
  • 72
  • 1
    I could be wrong, but I believe he wants to loop over options in a config file. I don't thing just using source will do what he wants. – Zoredache Jan 06 '11 at 08:51
  • Yeah, I thought of `source` too but I think he doesn't want it that way. – weeheavy Jan 06 '11 at 10:19
  • correct, the looping part is the important one, as i need to mass import some stuff.. – perler Jan 06 '11 at 15:18
  • In my `#!/bin/sh` scripts the `source` command wouldn't work. (MacOS) If I used the `.` instead it worked. – JaySon Jan 13 '22 at 11:10
3

Something like this. The important bit is using read to grab a line as an array.

#!/bin/bash
configfile=/pathtocontrolfile
cat $configfile | while read -a HR ; do

  [[ -z ${HR[0]} ]] && continue  # skip empty lines

  USER1=${HR[0]}
  HOST1=${HR[1]}
  PW1=${HR[2]}
  USER2=${HR[3]}
  HOST2=${HR[4]}
  PW2=${HR[5]}

  imapsync \
  --buffersize 8192000 --nosyncacls --subscribe --syncinternaldates --IgnoreSizeErrors \
  --host1 $HOST1 --user1 $USER1 --password1 $PW1 --ssl1 --port1 993 --noauthmd5 \
  --host2 $HOST2 --user2 $USER2 --password2 $PW2 --ssl2 --port2 993 --noauthmd5 --allowsizemismatch

done
Zoredache
  • 130,897
  • 41
  • 276
  • 420
  • 1
    +1 for making me look up the difference between `[` and `[[`. (http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS if the OP is interested.) Very useful. – SmallClanger Jan 06 '11 at 09:35
  • 2
    You could read directly into the variables: `read -r USER1 HOST1 PW1 ...`. Note that `-r` should almost always be used (it allows backslashes to be read literally). Piping into `while` creates a subshell (which may not be a problem here). You can avoid that by redirecting the file into `done`: `done < "$configfile"`. @SmallClanger: See also [BashFAQ/031](http://mywiki.wooledge.org/BashFAQ/031). – Dennis Williamson Jan 06 '11 at 16:33
  • @Dennis Williamson, of course you are right, but the above was what I already had in a working script, please excuse the slight laziness. :) – Zoredache Jan 06 '11 at 18:06
  • @Dennis Williamson - Ah, much clearer than the ABS. Thanks. Also, Ditto the `read -r` option. – SmallClanger Jan 06 '11 at 18:30
  • @Zoredache: like Dennis said, you should avoid the `cat $configfile | while ... done` by moving it over to `while ..... done < "$configfile"` so that all the variables you define or modify in the while are not just for the while's duration (if it is in a subshell) but exist after it as well. And it is one less "UUOC" (Useless Use Of Cat) – Olivier Dulac Apr 07 '16 at 06:06
1

I found working solution here: https://af-design.com/2009/07/07/loading-data-into-bash-variables/

if [ -f "$SETTINGS_FILE" ];then
    . "$SETTINGS_FILE"
fi
kasperd
  • 30,455
  • 17
  • 76
  • 124
Lukasz
  • 111
  • 2
0

I think you should be using something like getopts if you want to make your script intelligent, instead of just trying to read your arguments on the number basis.

Something like this.

#!/bin/bash

while getopts ":a" opt; do
  case $opt in
    a)
      echo "-a was triggered!" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done


$ ./go_test.sh -a
-a was triggered!
$ 

You can parse arguments also. You can read more details about the tutorial over here.

Napster_X
  • 3,373
  • 18
  • 20
  • Needs improvements: a more robust example could be : `OPTIND=1; SILENT=":"; while getopts "${SILENT}a:bc:" opt ; do case $opt in a) val=$OPTARG; ... ;; b) ... ;; c) val=$OPTARG; ... ;; :) echo "(silent mode): -${OP TARG} needs argument"; ... ;; ?) echo "(non-silent) : option not recognized"; ... ;; esac ; done ; shift $((OPTIND - 1)) ` . OPTIND is set to 1 so you can call the getopts multiple times if needed, and the shift makes sure you process the remaining args only. By default, processes the $1 ... $n, but can process other args if you put them after the "opt" and before the ";" above – Olivier Dulac Apr 07 '16 at 06:40