Using exec in find over ssh from shell script
Solution 1
You forgot to add an ';':
ssh -q $CUR_HOST "cd $LOGS_DIR; echo cd $LOGS_DIR; find . -name *.log -mmin +1440 -exec gzip {} \;; exit"
Because of the missing ;
, find interprets exit
as a path.
Solution 2
There are several syntax problems, one of which is fatal and another which is likely to bite you at some point.
By the way, you can use +
instead of ;
to end the -exec
directive; this way gzip
will be executed in batches, which is slightly faster.
If the value of LOGS_DIR
is /somewhere/with/logs
, then the following command is executed on the remote host:
cd /somewhere/with/logs; echo cd /somewhere/with/logs; find . -name *.log -mmin +1440 -exec gzip {} \; exit
or, substituting newlines for the equivalent semicolon operator:
cd /somewhere/with/logs
echo cd /somewhere/with/logs
find . -name *.log -mmin +1440 -exec gzip {} \; exit
When find
sees that spurious exit
after the -exec … ;
directive, it doesn't know what to do with it; it hazards a (wrong) guess that you meant it to be a path to traverse. You need a command separator: put another ;
after \;
(with or without a space before).
A second problem is that the wildcard pattern *.log
will be expanded in the directory /somewhere/with/logs
. If there is a matching file (i.e. if there is a file of the form /somewhere/with/logs/something.log
), the pattern will be replaced by that matching file. It's only if there is no match that *.log
remains unchanged by the remote shell and is seen by find. Add quotes around the pattern to prevent this.
A third problem is with the value of LOGS_DIR
. It is expanded locally into a string which is executed as a shell script on the remote side. So if there are any shell special characters (whitespace, $
, etc.) in it, this can execute an arbitrary remote command. A quick-and-dirty way to quote most characters is to put single quotes around the variable expansion; this will break only if the value contains single quotes. If your local script is running bash, you can replace single quotes by the 4-character sequence '\''
to protect the value of LOGS_DIR
:
ssh -q "$CUR_HOST" "cd '${LOGS_DIR//\'/\'\\\'\'}'; find . -name '*.log' -mmin +1440 -exec gzip {} +; exit"
Related videos on Youtube
Comments
-
Skittles over 1 year
So, I am attempting to execute the following from within a shell script;
ssh -q $CUR_HOST "cd $LOGS_DIR; echo cd $LOGS_DIR; find . -name *.log -mmin +1440 -exec gzip {} \; exit"
When this runs, it generates the following error;
find: paths must precede expression Usage: find [-H] [-L] [-P] [path...] [expression]
I've tried numerous variations based on things found on Google, but none seem to resolve this problem. Could anyone tell me what is wrong with this?
As a side note, when I manually login to the target host and run the same find command there, it works just fine. So, I'm led to think that it's something to do with the command being embedded in the double quotes of the ssh command.
-
Stéphane Chazelas almost 10 yearsQuote
*.log
('*.log'
) to prevent globbing in the remote shell. -
Skittles almost 10 yearsThanks...Do I need to also include the parens or just the single quotes?
-
Skittles almost 10 yearsI just tried '*.log', but still the same error.
-
-
Skittles almost 10 yearsThat's precisely what the doctor ordered! Makes sense now as the first semi-colon was escaped for the find exec. Thank you.
-
Stéphane Chazelas almost 10 yearsNote that if the login shell of the remote user is (t)csh, that escaping will still not work if $LOGS_DIR contains newline characters.
-
Skittles almost 10 yearsAlthough I already accepted the previously submitted answer, this one's verbosity is extremely admirable and highly appreciated! I would just like to personally extend a thanks to you for the detail to which you displayed in this answer.