I have a local and a remote host, both running Ubuntu, with the shell set to bash. In my home directory, I have two files, file-1
and file-2
, both on the local host, and on a remote host called remote
. There are some other files in each home directory, and I want to list only files matching file-*
.
Locally, these produce the expected result, file-1 file-2
:
$ ls file-*
$ bash -c 'ls file-*'
But these commands return ALL files in my home directory on the remote. What's going on there?
$ ssh remote bash -c 'ls file-*'
$ ssh remote bash -c 'ls "file-*"'
$ ssh remote bash -c 'ls file-\*'
$ ssh remote bash -c 'ls "file-\*"'
I know that simply ssh remote 'ls file-*'
produces the expected result, but why does ssh remote bash -c 'ls ...'
seem to drop the arguments passed to ls ...
? (I've also piped the output from the remotely executed ls, and it's passed along, so only the ls
seems to be affected: ssh remote bash -c 'ls file-* | xargs -I {} echo "Got this: {}"'
.)
Best Answer
The command being executed on the remote host when you use
ssh remote bash -c 'ls file-*'
isThat means
bash -c
executes the scriptls
. As positional parameters, thebash -c
script gets the names on the remote host matchingfile-*
(the first of these names will be put into$0
, so it's not really part of the positional parameters). The arguments won't be passed to thels
command, so all names in the directory are listed.ssh
passes the command on the the remote host for execution with one level of quotes removed (the outer set of quotes that you use on the command line). It is the shell that you invokessh
from that removes these quotes, andssh
does not insert new quotes to separate the arguments to the remote command (as that may interfere with the quoting used by the command).You can see this if you use
ssh -v
:The three other commands that you show works the same, but will only set
$0
to the stringfile-*
while not setting$1
,$2
, etc. for thebash -c
shell.What you may want to do is to quote the whole command:
Which, in the
ssh -v
debug output, gets reported asIn short, you will have to ensure that the string that you pass as the remote command is the command you want to run after your local shell's quote removal.
You could also have used
or