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:
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
'\''.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.
Use a tool like
printf %qif 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 thesshcase.
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'
