Home > OS >  Run relatively complex find|xargs command from a remote server
Run relatively complex find|xargs command from a remote server

Time:08-09

I have a command that I can run without any issues on a Linux server (let's call it server1).

Here it is

myuser@server1:  find -L /data* -type d -maxdepth 2 | xargs -d $'\n' sh -c 'for arg do echo "$arg" $(stat -f -L -c %T  "$arg") ; done'

This lists all the directories 2 levels deep in directories tree of server1 and shows associated file systems for each of them.

Now what I want to do is to run exact same command from a bash script that resides on a remote server (let's call it server2) after ssh-ing to server1, but have the output written to a file on server2. Basically do something like below

myuser@server2: ssh "myuser@server1"  "find -L /data* -type d -maxdepth 2 | xargs -d $'\n' sh -c 'for arg do echo "$arg" $(stat -f -L -c %T  "$arg") ; done'" >output.txt

However I can't seem to find the right syntax for this. I think the command above does variables expansion incorrectly. Could you please help?

Thank you, -Grigor

CodePudding user response:

The string you give to ssh as a remote command is subject to normal parsing on the local machine before it gets sent to the remote machine for reparsing and execution. To simplify your example a little:

# prints the numbers 1 2 3
for arg in 1 2 3 ; do echo $arg ; done
# prints three empty lines, assuming $arg is unset in your local environment
ssh user@remotehost "for arg in 1 2 3 ; do echo $arg ; done"

What happened here? When bash evaluates your command line in the second example, it substitutes $arg for its value following normal parameter expansion rules before it runs ssh. This means the command that actually gets sent through to the remote machine is for arg in 1 2 3 ; do echo ; done.

There's a lot of good advice for fixing this on the BashFAQ. A couple summary examples:

  1. Manual requoting. In my very simple example you can use single quotes instead of double quotes to protect the string from being evaluated. Your code is a little more complex since you have embedded single quotes, so you would have to handle escaping them by replacing every single quote with '\''.

  2. Use stdin instead of the command line. A heredoc for example will let you pass commands to the remote machine with less manual formatting required.

  3. Use a tool like printf %q if the remote shell is Bash. This will format a string in such a way that it is safe to be evaluated twice like it is in the ssh case.

CodePudding user response:

Instead of using xargs and pipe, how about rearranging the command:

ssh myuser@server1 \
'for d in $(find -L ~/data* -type d -maxdepth 2); do echo "$d $(stat -f -L -c %T $d)"; done'
  • Related