this successfully finds the file
No. Your first command only luckily finds the file. There are few problems with it. You were lucky it gave you a useful output.
In your manual approach
| xargs echo is almost a no-op. Almost, because it may introduce ambiguity. This may not be a problem if there is just one match and its name is not troublesome in this context. I won't explain what "troublesome in this context" means because even if
| xargs echo was completely fine, your original first command would still work without it anyway.
ssh command is somewhat flawed on its own. This is your command without the unneeded
ssh remoteuser@remotemachine find /home/remoteuser/exampledirectory/ -type f -name 'examplefile*'
examplefile* is quoted but it's not enough. The quotes prevent the local shell from globbing. Then
ssh recognizes the first operand that looks like a command to run on the remote side and builds an acutal command from it and every operand that follows. In your case these operands are
examplefile*. The last one is without quotes because the quotes was "used" by the local shell. In effect the remote command built by
ssh and run by a shell on the remote side is
find /home/remoteuser/exampledirectory/ -type f -name examplefile*
and this does trigger globbing on the remote side. In some circumstances this may give you an error or unexpected results (see this).
One interesting scenario. If
examplefile* is expanded by globbing on the remote side to
examplefile123.tar.gz (because such file happens to be in the current working directory and it's the only match), the
-name primary will get this exact string, not the pattern you typed, and the whole search will be limited to this exact filename. The command I present below is not flawed like this,
-name gets the pattern for sure. Depending on what files there are on the remote side, it may be my command will find more files than yours.
This is the right command:
ssh remoteuser@remotemachine 'find /home/remoteuser/exampledirectory/ -type f -name "examplefile*"'
Single-quotes embrace the entire code destined to the remote side. Double-quotes will survive and prevent globbing on the remote side.
You can store the result in a file on the local side like this:
ssh remoteuser@remotemachine 'find /home/remoteuser/exampledirectory/ -type f -name "examplefile*"' > filelist
Passing result to
There is this question: How to
rsync only a specific list of files? The best answer advises
--files-from. This is what
man 1 rsync says:
Using this option allows you to specify the exact list of files to transfer (as read from the specified
- for standard input). […]
Please read the entire relevant fragment of the manual, especially what it says about implied
-R) option is implied, which preserves the path information that is specified for each item in the file (use
--no-R if you want to turn that off).
After you run the last command provided above, you will have
filelist to use with
--files-from. An example command:
rsync -avzhe ssh --progress --files-from=filelist --no-relative remoteuser@remotemachine:/ /home/localuser/examplelocaldirectory
On the local side paths will be like
--no-relative you would get (on the local side) paths like
Note I used
/ as the source (remote) path. This is because you gave an absolute path to
find, so all paths in
filelist are absolute; but
rsync will treat them relative to the directory you specify. Therefore the tool should start in (remote)
/ to get to the right objects.
But if you used
find ./exampledirectory/ … then
rsync should be given
remoteuser@remotemachine:. (or just
remoteuser@remotemachine:) because all paths in
filelist would be relative to
remoteuser's home directory. In such case without
--no-relative the local path would be created like
And if the remote command was
cd exampledirectory && find . …
then all paths in
filelist would be relative to
rsync should use
--no-relative the local path would be
which looks like the one where
--no-relative was used. The option still makes a difference for subdirectories. If the (remote) file was
…/exampledirectory/foo/examplefile123.tar.gz then you would get
depending on whether or not you used
Hints and improvements
--files-from=- will make
rsync read its stdin. You want a single command, so this will be very useful:
ssh … 'find …' | rsync … --files-from=- …
You were talking about "the needed file", one file; but in general you may get multiple paths from
rsync will have no problem in processing them all. There are few ways to limit what you get:
- More restrictive
find … -print -quit (if
-quit is supported) to get just one result (but it may not be the one you expect).
- Filtering on the remote side. E.g.
ssh … 'find … | sort -R | head -n 1' to get just one result, randomly chosen.
- Filtering on the local side. E.g.
ssh … 'find …' | sort -R | head -n 1.
In general filtering on the remote side will reduce the amount of data your
ssh pulls. But if the remote machine is slow/limited and the filter stresses the CPU or requires lot of memory, you may want to pull more data and parse it on the local side.
In Linux filenames can contain newline characters. If this happens in your case then a single path will look like multiple entries in the list.
rsync will try to retrieve nonexistent (or wrong) files (or existing directories!). If
find on the remote side supports
-print0 (it may or may not), use it as the final action, along with
-0 option of
rsync. The entries will be null-terminated. The commands will look like this:
ssh … 'find … -print0' > filelist
rsync -0 …
Note if you want to process/filter the list before passing to
rsync, all tools you use must treat null characters as delimiters.
You can combine these hints. Example:
ssh … 'find … -print0 | head -z -n 1' | rsync -0 … --files-from=- …