You need to use declare -A
instead of declare -a
. You are clearly using associative arrays with arbitrary string arguments as indices, but declare -a
is only for integer indexed arrays. arg.txt
does not evaluate to a valid integer, hence your error.
Edit
You seem to be using bash
version 3. Unfortunately, associative arrays are not available until version 4. I recommend you post a sanitized version of your original deploy.sh
script with sensitive personal information removed so you can get ideas from other people about alternative approaches.
Edit 2
Just to summarize a bit of exchange in the chat:
The easiest way to do some action over all the arguments is to just iterate over them with a for
loop:
for arg; do
scp login1@host1:"$arg" login2@host2:/dest/
done
Remember to double-quote all instances of "$arg"
.
You do not need to put the arguments in an array yourself, as they already exist in the array $@
, which is what for
uses by default when you don't give an explicit in list...
.
The difference may be seen via strace
:
$ strace -ff -o bq watch sh -c 'ls\ /tmp/|wc -l'
^C
$ strace -ff -o nobq watch sh -c 'ls /tmp/|wc -l'
^C
$ grep exec bq* | grep sh
bq.29218:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls\\ /tmp/|wc -l"], [/* 54 vars */]) = 0
bq.29219:execve("/bin/sh", ["sh", "-c", "sh -c ls\\ /tmp/|wc -l"], [/* 56 vars */]) = 0
bq.29220:execve("/bin/sh", ["sh", "-c", "ls /tmp/"], [/* 56 vars */]) = 0
$ grep exec nobq* | grep sh
nobq.29227:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls /tmp/|wc -l"], [/* 54 vars */]) = 0
nobq.29228:execve("/bin/sh", ["sh", "-c", "sh -c ls /tmp/|wc -l"], [/* 56 vars */]) = 0
nobq.29229:execve("/bin/sh", ["sh", "-c", "ls", "/tmp/"], [/* 56 vars */]) = 0
In the backquote case, ls /tmp
is passed as a single argument to the -c
to sh
, which runs as expected. Without this backquote, the command is instead word split when watch
runs sh
which in turn runs the supplied sh
, so that only ls
is passed as the argument to -c
, meaning that the sub-subsh
will only run a bare ls
command, and lists the contents of the current working directory.
So, why the complication of sh -c ...
? Why not simply run watch 'ls /tmp|wc -l'
?
Best Answer
That's correct. It's the same issue as with storing commands in a variable (discussed e.g. here), it can't really be done nicely. The options that come to mind are:
cmd
output a NUL-separated list of filenames, then pipe toxargs -0
to pass them to another commandIFS
to some character that doesn't appear in any filenames, and havecmd
output the list of files separated by thatcmd
output the file names properly quoted for the shell, and then run it througheval
.Like so:
Or use
find -exec ...
if you can replacecmd
with that.