A question about command "find" - linux

find . \( -name "_*" -or -newer while2 \) -type f -print
In the linux command above , why _* should be quoted ?

First, the shell expands all unquoted *'s to match file names in the local directory.
After that, the shell runs the find command with that list of file names.
Generally, you don't want the shell to do '*' globbing in the local directory. Generally, you wind the find command to do filename matching in other directories.
The shell has several other things it does before running a command. $VARIABLE replacement is one of those.

Why _* should be quoted ?
If it isn't, your shell might expand the * to be substituted with files in the current directory. That's probably not what you wanted here.

if you don't quote it, the shell (bash?) would try to expand. if there's any file that starts with '_' on the current directory, you'd get something like:
find . \(-name _somefile _someother _file3 .... -or -newer while2 \) -type f -print
certainly not what you want. i always quote patterns, just to be sure the shell won't mess with them

Related

Unix brace expansion within find command

This is working fine:
$ echo email_{ldn,nyk,asp}.log
Similarly, I wanted to find filenames which differ slightly:
$ find ~ -type f -name email_{ldn,nyk,asp}.log
But above command results in error:
find: paths must precede expression: email_nyk.log
Any help on brace expansions within find command will be very much appreciated.
The error message results from the expansion of the pattern by your shell.
Assuming you have the files email_ldn.log, email_nyk.log and email_asp.log in your current directory, your command
find ~ -type f -name email_{ldn,nyk,asp}.log
will be expanded to
find ~ -type f -name email_ldn.log email_nyk.log email_asp.log
which results in the error message.
To prevent the expansion of a pattern by the shell you have to quote the pattern. Unfortunately, find doesn't support patterns with a list of alternatives in braces, so using this pattern with find will not work as you might expect.
find ~ -type f -name "email_{ldn,nyk,asp}.log" # Does not work as intended.
find ~ -type f -name "email_*.log" # This would work, but matches other files as well.
If you have GNU find, you could use a regular expression instead.
find ~ -type f -regextype posix-extended -regex ".*/email_(ldn|nyk|asp).log"
Brace expansion is not the way to do this sort of thing, and eval is evil, but you could do:
eval find ~ -type f -and \\\( -false "-or -name "email_{ldn,nyk,asp}.log \\\)
The main idea is that -name email_ldn.log email_nyk.log email_asp.log does not work, because you want the expression to be -name email_ldn.log -or -name email_nyk.log -or -name email_asp.log, so you create that expression with the brace expansion. But then find receives -or -name email_???.log as a single argument instead of 3 arguments, so you need to force word splitting with eval. Overall, a nasty, ugly solution.
If you want to find all files with one prefix you can use this command:
find ~ -type f -name "email_*.log"
In this situation you don't need to use braces. Don't forget to put " (quotes) before and after the pattern of the name.
find doesn't support braces to set alternatives, like the shell does. So, if you want to find specific files, you can do this in 2 steps: first find all files with 'email' prefix and then grep for specific files. Try this:
find ~ -type f -name "email*" | egrep "email_(ldn|asp|nyk).log"
It was realized eventually during the conquest, some of you have suggested using eval, but I think it would better to use xargs :
As the aim is to find files matching the brace expansion and execute few command thereafter, so xargs suites this purpose nicely.
$ ls ~/email_{ldn,nyk,asp}.log | xargs -I %% sh -c 'chomd 644 %% && cp -arf %% ~/feed_dir'

How do I find a file containing using wildcard on Linux?

I would like to know if it is possible to use find with wildcards:
I use this command, but I have an error
find -type f -name /target/*.zip
You need to put the wildcard in quotes, otherwise it gets expanded by the shell before the command is run.
And it should just be a filename, not a pathname. The directory to start searching should be an argument to find before the filter specifications.
find /target -type f -name '*.zip'

launch several scripts located in subdirectories

Here is my problem. I have a directory that contains multiple sub-directories. In each sub-directory, there is at least one script sh.
I want to do a script that execute sequentially all this scripts.
I am pretty new to linux.
Thanks for your help,
find . -name "*.sh" -exec {} \;
This is a shell command which, beginning in the directory it's being run in (specified by .), finds file names that end in .sh and then executes those files (the found file is substituted in the {}). The backslash prevents the semicolon from being expanded by the shell (here, bash).
Try doing it using find and for:
for file in `find . -type f -name "*.sh"`; do sh $file; done
Use can also store it in array and do it:
array=($(find . -type f -name "*.sh"))
for file in ${array[#]};do sh $file; done
From the top directory, run the following command:
for f in `find . -type f -name \*.sh`; do $f; done
The find command will locate all .sh files. The output of the find command (a whitespace separated list of pathnames to the scripts) becomes the input to the for command. The for command processes each input, assigning each entry to the variable f. The "$f" executes each script.

wild cards on find and ls

I'm trying to figure out the wild-cards to do file operations.
I have these files in a directory for testing purposes:
file_BSD.GIF file_linux.gif file_unix
See my ls command,
$ ls *{.GIF,.gif}
file_BSD.GIF file_linux.gif
Which is OK.
But "find" doesn't seem to work the same way:
$ find -name *{.GIF,.gif}
find: paths must precede expression: file_linux.gif
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
By the way, I've read that "-iname" should locate both the uppercase and lowercase files, but that doesn't seem to work either:
$find -iname *.gif
./file_linux.gif
(This should locate the .GIF file as well, right?).
find -name *{.GIF,.gif} is wrong.
This command is first expanded by the shell to find -name *.GIF *.gif
Then further expanded to :
find -name file_BSD.GIF file_linux.gif
# as you have only these files in directory
Now this -name file_BSD.GIF file_linux.gif is passed to find. And this is wrong as there is no switch like file_linux.gif that is accepted by find.
What you need is this command.
find -name '*.GIF' -or -name '*.gif'
Assuming you want to collect .gif files in a case insensitive manner, this find command becomes,
find -iname '*.gif'
Note the single quotes (') here. It means *.GIF should be sent to find as is without any shell expansion. And find will use this as pattern. This single quote is necessary unless you escape the shell meta-characters. In that case the command would look like
find -iname \*.gif
You are having trouble with the parameter -iname of find because you must quote the patterns you give to it.
So, you should do:
find -iname '*.gif'
This is stated in the manual:
"... Please note that you should quote patterns as a matter of course, otherwise the shell will expand any wildcard characters in them."
You should understand that (in contrast to Windows) the shell is expanding the *{.GIF,.gif} before passing it to the find program.
You can feel what the shell does by replacing the program with echo.
So you should quote the program argument, like
echo \-name '*{.GIF,.gif}'
so run
find -name '*.{GIF,gif}'
Maybe you want
find -name '*.gif' -o -name '*.GIF'
Please read the Advanced Bash Scripting Guide (and perhaps the execve(2) man page, to understand how the kernel run programs).

redirecting output of 'find' command to 'vim'

I am doing a find $PWD -name 'filename' | vim -
expecting the file filename to be opened in vim editor. but it is not working.
In this case, I am sure that there exists just one file with name 'filename'.
Also the result of find gives the complete path on stdout.
vim "$(find "$PWD" -name 'filename')"
or
find "$PWD" -name 'filename' -exec vim {} \;
(You can drop "$PWD", by the way. find starts the search from current directory by default.)
find . -name 'filename' -print0 | xargs -0 vim
should also work. You might want to read up on xargs, which is a handy thing to know about.
Mentioned in #idbrii's comment, but my favorite is:
find . -name 'filename' -type f -exec vim {} \+
This opens up each file found in its own buffer ready to be navigated with :next and :prev. Tested on OSX, but I'm fairly certain it will work on Linux too.
One way I find is very easy is enclosing the find command with backticks (character under tilde on most keyboards) and passing it to vim.
vim `find . -name myfile`
In fact, you can use backtick for any command to get the literal string output of the command.

Resources