Error = find: -exec: no terminating ";" or "+" - macos

I am looking for some help trying to get a command working. I want to find some files only and move them, but when I enter this command:
find /Volumes/NEXSAN/Engine\ Folders/Input/DTO_Proxy/* -type f -mtime +7 -exec mv -v {} /Volumes/NEXSAN/.2BeDeleted4realz/
I get this error
find: -exec: no terminating ";" or "+"
I know I probably have it wrong, but I can't figure out what's missing?

Just terminate the find command with \;, making sure to include the space before the \;.
find /Volumes/NEXSAN/Engine\ Folders/Input/DTO_Proxy/* -type f -mtime +7 -exec mv -v {} /Volumes/NEXSAN/.2BeDeleted4realz/ \;

If you want to correct the find command that you had, it should look like this:
find . -name '*.xml' -exec SetFile -t TEXT {} \;
The *.xml needs to be quoted so it's passed as a parameter to find instead of expanded by the shell. The ; also needs to be escaped so it's passed as part of the parameter to find and not interpreted by the shell.
Keep in mind this will only work for files within the current directory (and subdirectories) and for any new files created, you would need to run the command again.

Related

Find command output to echo without variable assignment, in one line

I'm trying to write one line of code that finds all .sh files in the current directory and its subdirectories, and print them without the .sh extension (preferably without the path too).
I think I got the find command down. I tried using the output of
find . -type f -iname "*.sh" -print
as input for echo, and formatting it along these lines
echo "${find_output%.sh}"
However, I cannot get it to work in one line, without variable assigment.
I got inspiration from this answer on stackoverflow https://stackoverflow.com/a/18639136/15124805
to use this line:
echo "${$( find . -type f -iname "*.sh" -print)%.sh}"
But I get this error:
ash: ${$( find . -type f -iname "*.sh" -print)%.sh}: bad substitution
I also tried using xargs
find . -type f -iname "*.sh" -print |"${xargs%.sh}" echo
But I get a "command not found error" -probably I didn't use xargs correctly, but I'm not sure how I could improve this or if it's the right way to go.
How can I make this work?
That's the classic useless use of echo. You simply want
find . -type f -iname "*.sh" -exec basename {} .sh \;
If you have GNU find, you can also do this with -printf.
However, basename only matches .sh literally, so if you really expect extensions with different variants of capitalization, you need a different approach.
For the record, the syntax you tried to use for xargs would attempt to use the value of a variable named xargs. The correct syntax would be something like
find . -type f -iname "*.sh" -print |
xargs -n 1 sh -c 'echo "${1%.[Ss][Hh]}"' _
but that's obviously rather convoluted. In some more detail, you need sh because the parameter expansion you are trying to use is a feature of the shell, not of echo (or xargs, or etc).
(You can slightly optimize by using a loop:
find . -type f -iname "*.sh" -print |
xargs sh -c 'for f; do
echo "${f%.[Ss][Hh]}"
done' _
but this is still not robust for all file names; see also https://mywiki.wooledge.org/BashFAQ/020 for probably more than you realized you needed to know about this topic. If you have GNU find and GNU xargs, you can use find ... -print0 | xargs -r0)

Passing a command to 'find -exec' through a variable does not work

given a directory $HOME/foo/ with files in it.
the command:
find $HOME/foo -type f -exec md5deep -bre {} \;
works fine and hashes the files.
but, creating a variable for -exec does not seem to work:
md5="md5deep -bre"
find $HOME/foo -type f -exec "$md5" {} \;
returns: find: md5deep -bre: No such file or directory
why?
Since you are enclosing your variable in double quotes, the entire string gets sent to find as a single token following -exec and find treats it as the name of the command. To resolve the issue, simply remove the double quotes around your variable:
find "$HOME/foo" -type f -exec $md5 {} \;
In general, it is not good to store commands in shell variables. See BashFAQ/050.
Use an array.
md5Cmd=(md5deep -bre)
find "$HOME/foo" -type f -exec "${md5Cmd[#]}" {} \;
Better still, make the whole -exec statement optional:
md5Cmd=( -exec md5deep -bre {} \; )
find "$HOME/foo" -type f "${md5Cmd[#]}"
I have found the syntax for find -exec a bit weird (with several pitfalls as the ones #codeforester has mentioned).
So, as an alternative, i tend to separate the search part from the action part by piping the output of find (or grep) to a proper xargs process.
For example, i find it more readable (-n1 for using exactly 1 argument per command):
find $HOME/foo -type f | xargs -n1 md5deep -bre

bash function not executing, returns "command not found"

In my .bash_profile, I have a function that returns all php files containing the parameter string passed in:
summon() {
"find . -name '*.php' -exec grep -ril '$1' '{}' \;"
}
When I am on my command line (mac) and I run summon foo, I get the error:
-bash: find . -name '*.php' -exec grep -ril 'foo' '{}' \;: command not found
But if I just copy/paste the find . -name '*.php' -exec grep -ril 'foo' '{}' \; into the command line, then it works properly, returning all of the php files that contain the string 'foo'.
Does anyone have any idea why the function is not being evaluated?
Just remove the quotes from your summon function. By quoting it, you are telling it to look for a command called find . -name '*.php' -exec grep -ril '$1' '{}' \; rather than a command called find with arguments of . -name '*.php' -exec grep -ril '$1' '{}' \; There is a good reason for this; consider if there were an application whose name contained a space (let's call it foo bar). If not for this quoting syntax, the program would be more difficult to execute from bash, because typing foo bar would try to run the command foo with argument bar, as opposed to running foo bar (As a side note, if this were the case, you could also run it by escaping the space: foo\ bar). Of course, it is considered bad form to name an executable something containing a space for this reason of adding complexity to run the command.
Your function should look like this:
summon() {
find . -name '*.php' -exec grep -ril "$1" '{}' \;
}
Also see #gniourf_gniourf 's comment on this answer with a few more suggestions, including using -type f on the find command to limit the search to files and removing the unnecessary -r flag from grep, because all files passed there will be files.
Loose the double-quotes around the find within the function.
summon() {
find . -name '*.php' -exec grep -il "$1" '{}' +
}
Within double-quotes, shell tries to expand it, so that it can evaluate it as an expression, Shell-Expansion
Argument inside single quote is the problem. Try like the below
summon() {
find . -name '*.php' -exec grep -ril "$1" {} \;
}

What's the difference between `\;` and `+` at the end of a find command?

These are bash commands that are used to convert tabs to spaces.
Here's the link to the original stackoverflow post.
This one uses \; at the end of the command
find /path/to/directory -type f -iname '*.js' -exec sed -ie 's|\t| |g' '{}' \;
This one uses + instead of \;.
find /path/to/directory -type f -iname '*.js' -exec sed -ie 's|\t| |g' '{}' '+'
What exactly is the difference between the two?
The \; or + is not related to bash. It's an argument to the find command, specifically to find's -exec option.
find -exec uses {} to pass the current file name to the specified command, and \; to mark the end of the the command's arguments. The \ is needed because ; by itself is special to bash; by typing \;, you can pass a literal ; character as an argument. (You can also type ';' or ";".)
The + symbol (no \ needed because + is not special to bash) causes find to invoke the specified command with multiple arguments rather than just once, in a manner similar to xargs.
For example, suppose the current directory contains 2 files named abc and xyz. If you type:
find . -type f -exec echo {} \;
it invokes the echo command twice, producing this output:
./abc
./xyz
If you instead type:
find . -type f -exec echo {} +
then find invokes echo just once, with the following output:
./xyz ./abc
For more information, type info find or man find (if the documentation is installed on your system), or you can read the manual online at http://www.gnu.org/software/findutils/manual/html_node/find_html/

How to add .txt to all files in a directory using terminal

I have many files without file extention. Now I want to add .txt to all files. I tried the following but it gives an error, mv: rename . to ..txt: Invalid argument.
How can I achieve this?
find . -iname "*.*" -exec bash -c 'mv "$0" "$0.txt"' {} \;
You're nearly there!
Just add -type f to only deal with files:
find . -type f -exec bash -c 'mv "$0" "$0.txt"' {} \;
If your mv handles the -n option, you might want to use it (that's the option to not overwrite existing files).
The error your having is because . is one of the first found by found, and your system complains (rightly) when you want to rename .! with the -type f you're sure this won't happen. Now if you wanted to act on everything inside your directory, you would, e.g., add -mindepth 1 at the beginning of the find command (as . is considered depth 0).
It is not very clear in your question, but what if you want to add the extension .txt to all files that don't have an extension? (we'll agree that to have an extension means to have a period in the name). In this case, you'll use the negation of -name '*.*' as follows:
find . -type f \! -name '*.*' -exec bash -c 'mv "$0" "$0.txt"' {} \;

Resources