find: missing argument to `-exec' with -exec scp {} - shell

I wrote a script which will login to Prod server and find a file and copy that to StandBy server.
My script is:
# ! /bin/ksh
ssh -l oracle -p 20022 IP_Address "find /opt/oracle/wcm/backup/export/ -name "*.DMP" -mtime -1 -exec scp {} oracle#IP_Address:/opt/oracle/wcm/impdumps"
After running the script I am getting:
find: missing argument to `-exec'
I looked all the document saying put '{}' or \; at the end of line, but nothing works. What am I doing wrong?

You need to end the -exec with an escaped command separator. From the manual:
-exec command ;
Execute command; true if 0 status is returned. All following
arguments to find are taken to be arguments to the command until
an argument consisting of ‘;’ is encountered. The string ‘{}’
is replaced by the current file name being processed everywhere
it occurs in the arguments to the command, not just in arguments
where it is alone, as in some versions of find. Both of these
constructions might need to be escaped (with a ‘\’) or quoted to
protect them from expansion by the shell.
What you're doing should work; you suggested you'd tried to put '\; at the end of line', but it's not clear if you did it at the end of the whole combined command string:
ssh ... "find ..." \;
which wouldn't work and would still give the 'missing argument' error; or at the end of the find command, i.e. within the quoted command you're passing as an argument to ssh:
ssh ... "find ... \;"
You also have two potential layers of shell expansion. The \; will be expanded by your local shell, so the remote shell can see it as just ;. You might need to escape it twice, I think, though it works for me either way.
You should also escape the quotes around your filename pattern.
ssh -l oracle -p 20022 IP_Address "find /opt/oracle/wcm/backup/export/ -name \"*.DMP\" -mtime -1 -exec scp {} oracle#IP_Address:/opt/oracle/wcm/impdumps \\;"
You might find it simpler to run this command directly on your prod server rather than remotely, but I have no idea why you're choosing to do that; or to run rsync locally on your prod server if you're doing this regularly. If you stick with find you should note the warning in the manual about the security problems, and consider using -execdir.

Related

Shell script - find: missing argument to `-exec'

I'm trying to run this script:
alias logs="cd L:/"
find $logs -name '*system.log*' -mtime +14 -exec rm {} \;
But get this error: find: missing argument to `-exec'. I've tried looking at other posts on this but can't get it working. I'm using cygwin to run this script on Windows.
If you have created any alias with name log, then to use it, you should be using $logs and not logs
Also, maybe the find you are using is of Windows and not of Cygwin. This is made clear if you type 'which find'
First, some minor fixes:
#!/bin/sh
logs=/cygdrive/l
find "$logs" -name '*system.log*' -mtime +14 -exec rm -- {} +
...but those won't address your real problem (the one causing -exec to report an error), which is almost certainly the presence of DOS newlines in your script.
find -exec reports the error in question when it doesn't see an argument containing only the exact string ;. Outside quotes, \; should be that argument -- but it can be different if your file has hidden characters. And a DOS text file will appear to have hidden characters when opened by a program expecting a UNIX text file, because the two formats have different line separators.
To fix this, open the file in a native-UNIX editor such as vim, run :set fileformat=unix, and save it; or use dos2unix to convert it in-place.

how can i copy all the xml files which is having current date as filename from all directories

I want to copy all the xml files which is having current date as file name from all directories. Below is the script i have written.
#!/bin/bash
CURRENT_DATE=`date +'%d%m%Y'`
Temp_Path=/appinfprd/bi/infogix/IA83/InfogixClient/Scripts/IRP/New_Vendors/
FILE_PATH=/bishare/DLSFTP/DLSTREAM/
FILE_DATE=`date -d "-2 days" +"%Y%m%d"`
cd $FILE_PATH
find . -name '*$FILE_DATE*.xml' -exec cp $Temp_Path
But it is not working.
Your find statement is wrong. You should end it with \; to indicate the end of the exec command and put {} where the name of your file found should come in the command. So, you want :
find . -name "*$FILE_DATE*.xml" -exec cp "{}" "$Temp_Path" \;
Edit
As stated in the comments, there were also a problem in your initial post with your single quotes that should be double quotes. You might be interested by this man page. In particular by these sections :
-exec command ;
Execute command; true if 0 status is returned. All following arguments to find are taken to be arguments to the command until an argument consisting of ; is encountered. The string {} is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a \) or quoted to protect them from expansion by the shell. See the EXAMPLES section for examples of the use of the -exec option. The specified command is run once for each matched file. The command is executed in the starting directory. There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.
-exec command {} +
This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invocations of the command will be much less than the number of matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of {} is allowed within the command, and (when find is being invoked from a shell) it should be quoted (for example, '{}') to protect it from interpretation by shells. The command is executed in the starting directory. If any invocation returns a non-zero value as exit status, then find returns a non-zero exit status. If find encounters an error, this can sometimes cause an immediate exit, so some pending commands may not be run at all. This variant of -exec always returns true.

Use find exec with sed to replace part of file name and copy it

I have some settings file which need to go to appropriate location.
eg. settings.location1, settings.location2, settings.location3 which need to go to the appropriate folders namely location1, location2 and location3 and be renamed as simple settings.
eg.
/some/path/settings.location1 -> /other/path/location1/settings
/some/path/settings.location2 -> /other/path/location2/settings
I came up with the following comand:
find /some/path/settings.* -exec cp {} /other/path/$(echo {} | sed 's/.*\.//')/settings \;
But for some reason the sed does not get executed. And the result is
cp: cannot create regular file `/other/path//some/path/settings.location1/settings': No such file or director
It seems that if I run them separately all commands get executed well, but not if I put in exec. What am I doing wrong?
to make this work you need to create the intermediate folder when they are not present in the file system.
You can try:
find /some/path/settings.* -exec mkdir -p /other/path/$(ls -m1 {})/ && cp {} /other/path/$(ls -m1 {})/settings \;
I solved this by writing a very simple shell script which modified its parameters and did the operation, then having find do an exec on that script, passing {} multiple times.
In terms of your action, I think it would be something like:
find /some/path/settings.* -exec /bin/bash -c 'cp "$0" \
/other/path/"${1:s^/some/path/settings.^^}"/settings' {} {} \;
N.B. I inserted a line break after "$1" for ease of reading, but this should all be on one line. And I added double quotes around paths in case there are spaces in your paths. You might not need that precaution.
The Bash man page says,
-c If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, the first argument is assigned to $0 and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets the name of the shell, which is used in warning and error messages.
The contents of the -exec script are: the bash interpreter path, the -c option, a quoted argument, find's path {}, then find's path {} again.
The quoted argument contains the shell script. Bash sets the next two arguments to $0 and $1, then runs the shell script. The script uses history substitution ${1:s^/some/path/settings.^^} to modify the path from find to keep only the part you need.

Bash find: changing matched name for use in -exec

I'm writing a deploy script, and I need to run a less compiler against all .less files in a directory. This is easy to do with the following find command:
find -name "*.less" -exec plessc {} {}.css \;
After running this command on a folder with a file named main.less, I'm left with a file named main.less.css, but I want it to be main.css.
I know I can easily strip the .less portion of the resulting files with this command: rename 's/\.less//' *.css but I'm hoping to learn something new about using -exec.
Is it possible to modify the name of the file that matches while using it in the -exec parameter?
Thanks!
Your find command is using a couple of non standard GNU extensions:
You do not state where to find, this is an error in POSIX but GNU find select the current directory in that case
You use a non isolated {}, POSIX find doesn't expand it in that case.
Here is a one liner that should work with most find implementations and fix your double extension issue:
find . -name "*.less" -exec sh -c "plessc \$0 \$(dirname \$0)/\$(basename \$0 less)css" {} \;
On Solaris 10 and older, sh -c should be replaced by ksh -c if the PATH isn't POSIX compliant.
No, it is not possible to do it directly. You can only use {} to directly insert the full filename. However, in exec, you COULD put in other things like awk. Or you can redirect output to another program via pipes.
From the find man page:
-exec command ;
Execute command; true if 0 status is returned. All following
arguments to find are taken to be arguments to the command until
an argument consisting of `;' is encountered. The string `{}'
is replaced by the current file name being processed everywhere
it occurs in the arguments to the command, not just in arguments
where it is alone, as in some versions of find. Both of these
constructions might need to be escaped (with a `\') or quoted to
protect them from expansion by the shell. See the EXAMPLES
section for examples of the use of the -exec option. The
specified command is run once for each matched file. The command
is executed in the starting directory. There are unavoidable
security problems surrounding use of the -exec action; you
should use the -execdir option instead.

Unable to understand the syntax of the command find

The find command seems to differ from other Unix commands.
Why is there the empty curly brackets and a backward flash at the end of the following command?
find * -perm 777 -exec chmod 770 {} \;
I found one reason for the curly brackets but not for the backward flash.
The curly brackets are apparently for the path
Same as -exec, except that ``{}'' is
replaced with as many pathnames as
possible for each invocation of
utility
The -exec command may be followed by any number of arguments that make up the command that is to be executed for each file found. There needs to be some way to identify the last argument. This is what \; does. Note that other things may follow after the -exec switch:
find euler/ -iname "*.c*" -exec echo {} \; -or -iname "*.py" -exec echo {} \;
(This finds all c-files and python files in the euler directory.)
The reason that exec does not require the full command to be inside quotes, is that this would require escaping a lot of quotes inside the command, in most circumstances.
The string {} in find is replaced by the pathname of the current file.
The semicolon is used for terminating the shell command invoked by find utility.
It needs to be escaped, or quoted, so it won't be interpreted by the shell, because ; is one of the special characters used by shell (list operators).
See also: Why are the backslash and semicolon required with the find command's -exec option?
The (escaped) semicolon is needed so that "find" can tell where the arguments to the exec'd program end (if there are any) and additional arguments to "find" begin.
I'd recommend that you instead do that as
find . -perm 777 -print0 | xargs -0 chmod 770
"xargs" says to take the results of the find and feed it 20 at a time to the following command.

Resources