Unix brace expansion within find command - linux

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'

Related

Can't find a file by pattern [duplicate]

I am having a hard time getting find to look for matches in the current directory as well as its subdirectories.
When I run find *test.c it only gives me the matches in the current directory. (does not look in subdirectories)
If I try find . -name *test.c I would expect the same results, but instead it gives me only matches that are in a subdirectory. When there are files that should match in the working directory, it gives me: find: paths must precede expression: mytest.c
What does this error mean, and how can I get the matches from both the current directory and its subdirectories?
Try putting it in quotes -- you're running into the shell's wildcard expansion, so what you're acually passing to find will look like:
find . -name bobtest.c cattest.c snowtest.c
...causing the syntax error. So try this instead:
find . -name '*test.c'
Note the single quotes around your file expression -- these will stop the shell (bash) expanding your wildcards.
What's happening is that the shell is expanding "*test.c" into a list of files. Try escaping the asterisk as:
find . -name \*test.c
From find manual:
NON-BUGS
Operator precedence surprises
The command find . -name afile -o -name bfile -print will never print
afile because this is actually equivalent to find . -name afile -o \(
-name bfile -a -print \). Remember that the precedence of -a is
higher than that of -o and when there is no operator specified
between tests, -a is assumed.
“paths must precede expression” error message
$ find . -name *.c -print
find: paths must precede expression
Usage: find [-H] [-L] [-P] [-Olevel] [-D ... [path...] [expression]
This happens because *.c has been expanded by the shell resulting in
find actually receiving a command line like this:
find . -name frcode.c locate.c word_io.c -print
That command is of course not going to work. Instead of doing things
this way, you should enclose the pattern in quotes or escape the
wildcard:
$ find . -name '*.c' -print
$ find . -name \*.c -print
Try putting it in quotes:
find . -name '*test.c'
I see this question is already answered. I just want to share what worked for me. I was missing a space between ( and -name. So the correct way of chosen a files with excluding some of them would be like below;
find . -name 'my-file-*' -type f -not \( -name 'my-file-1.2.0.jar' -or -name 'my-file.jar' \)
I came across this question when I was trying to find multiple filenames that I could not combine into a regular expression as described in #Chris J's answer, here is what worked for me
find . -name one.pdf -o -name two.txt -o -name anotherone.jpg
-o or -or is logical OR. See Finding Files on Gnu.org for more information.
I was running this on CygWin.
You can try this:
cat $(file $( find . -readable) | grep ASCII | tr ":" " " | awk '{print $1}')
with that, you can find all readable files with ascii and read them with cat
if you want to specify his weight and no-executable:
cat $(file $( find . -readable ! -executable -size 1033c) | grep ASCII | tr ":" " " | awk '{print $1}')
In my case i was missing trailing / in path.
find /var/opt/gitlab/backups/ -name *.tar

List files that start with one or two numeric characters and a dash [duplicate]

This question already has answers here:
use find to match filenames starting with number
(3 answers)
Closed 1 year ago.
I want to select all the files in the current folder that start with either one or two numerical character.
e.g.:
1- filenameA
2- filenameB
....
17- filenameT
18- filenameU
I would like to know how I select only the files that start with up to 2 numerical characters.
So far I have tried
$ find . -name '[0-9]{1,2}*'
But it doesn't return anything, which I don't understand why. My reasoning when writing this command was:
[0-9] select any string starting with a number from 0 to 9
{1,2} and this can be repeated 1x or 2x
What am I getting wrong?
my INelegant solution so far
run the below two commands to tackle first the [0-9] range and then [10-99] range
$ find . -name '[0-9]-*'
$ find . -name '[0-9][0-9]-*'
You don't need find for that.
$ touch 7-foo 42-bar 69-baz
$ printf '%s\n' [0-9]{,[0-9]}-*
7-foo
42-bar
69-baz
$ shopt -s nullglob
$ printf '%s\n' {0..99}-*
7-foo
42-bar
69-baz
You can use gnu find like this with -regex option:
find . -maxdepth 1 -type f -regex '.*/[0-9][0-9]?-.*'
Details:
-maxdepth 1: In current directory only
-type f: Match files only
-regex '.*/[0-9][0-9]?-.*': Match 1 or 2 digits in filename at the start before matching a -
If you don't have gnu find then you may use:
find . -maxdepth 1 -type f \( -name '[0-9][0-9]-*' -o -name '[0-9]-*' \)
In your initial code, you are trying to use a regular expression where find's -name is expecting you to use shell pattern matching notation.
Your original "INelegant solution" using -name '[0-9]*' will fail because [0-9]* matches all files starting with a digit not just those with only one digit. Your updated solution should work better and can be written as a single command:
find \( -name '[0-9]-*' -o -name '[0-9][0-9]-*' \) ...
Alternatively, with POSIX find, you could search for filenames that start with a digit but exclude any whose third character is a digit:
find . -type f -name '[0-9]*' ! -name '??[0-9]*'
To not descend into sub-directories is slightly complicated if your find does not have -maxdepth option:
find . ! -name . -prune -type f -name '[0-9]*' ! -name '??[0-9]*'
! -name . matches everything except the starting directory. Applying -prune to them avoids the sub-directory descent.

Find all files contained into directory named

I would like to recursively find all files contained into a directory that has name “name1” or name “name2”
for instance:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name1/subfolder/file1s.a
structure/of/dir/name1/subfolder/file2s.b
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
structure/of/dir/name2/subfolder/file1s.a
structure/of/dir/name2/subfolder/file2s.b
structure/of/dir/name3/name1.a ←this should not show up in the result
structure/of/dir/name3/name2.a ←this should not show up in the result
so when I start my magic command the expected output should be this and only this:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
I scripted something but it does not work because it search within the files and not only folder names:
for entry in $(find $SEARCH_DIR -type f | grep 'name1\|name2');
do
echo "FileName: $(basename $entry)"
done
If you can use the -regex option, avoiding subfolders with [^/]:
~$ find . -type f -regex ".*name1/[^/]*" -o -regex ".*name2/[^/]*"
./structure/of/dir/name2/file1.a
./structure/of/dir/name2/file3.c
./structure/of/dir/name2/subfolder
./structure/of/dir/name2/file2.b
./structure/of/dir/name1/file1.a
./structure/of/dir/name1/file3.c
./structure/of/dir/name1/file2.b
I'd use -path and -prune for this, since it's standard (unlike -regex which is GNU specific).
find . \( -path "*/name1/*" -o -path "*/name2/*" \) -prune -type f -print
But more importantly, never do for file in $(find...). Use finds -exec or a while read loop instead, depending on what you really need to with the matching files. See UsingFind and BashFAQ 20 for more on how to handle find safely.

Why does find . -name foo* fail?

OK, probably a stupid question, but why doesn't this work?
find . -name Orna* -ls
It seems that "find" should be able to handle this simple request... Am I missing something here? This should be a basic execution of the "find" command, but linux is being stupid, or maybe I am.
correct way of using Find Command are the following phrases
find . -type f -name "filename" # this command used to find files from the curent dir
find . -type d -name "dir name" # this command used to find dirs from the curent dir
find /. -type f -name "filename" # this command used to find files from the system
find /. -type d -name "dir name" # this command used to find dirs from the system
I wish it be a helpful for you
You need to quote the name parameter so the shell doesn't expand the wildcard, e.g.
find . -name "Orna*" -ls
To explain the "why" a little more than existing answers do -- wildcards are expanded by the shell before the command being invoked is run. Thus, let's say your current directory contains files Orna1 and Orna2.
In that case, when you run
find . -name Orna* -ls
...what's actually invoked by the shell is:
find . -name Orna1 Orna2 -ls
...thus, find never sees the wildcard expression at all!
Quoting the expansion, as in:
find . -name 'Orna*' -ls
...prevents the shell from trying to expand the wildcard before running your command, thus preventing this issue.

A question about command "find"

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

Resources