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

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.

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

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 to specify 2 file name conditions in shell "find" cmd?

I need to search for all files - in cur dir and all subdirs with a name ending by ~, or a name that start and end by #, delete all files found.
this not working, it does not display found files
find -type f -name "~" or "#*#" ls -a -delete
and this is not working with piping:
find -type f -name "~" or "#*#" | ls -a | -delete
how to specify both conditions?
EDIT: The command line will find files matching the condition, print and then delete them.
Use -o (=or; the implicit default is -a = and) to combine operators and \(/\) to specify operator precedence:
find -type f \( -name "~" -o -name "#*#" \) -delete
You can combine multiple actions, e.g. outputting and deleting at the same time by simply providing all required actions:
find -type f \( -name "~" -o -name "#*#" \) -print -delete
An other option would be to simply filter the results with grep
find -type f | grep -E '(~|#*#)' | xargs rm
That would also allow to build additional steps (like displaying the files) into it

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.

how to exclude few folder levels in the FIND command results - unix

Following is the folder structure
- home/ABCD/test1/example1/sample1/textfile.txt
If I execute the find command like
find /home/ABCD/ -type f -print
I am getting the following output
/home/ABCD/test1/example1/sample1/textfile.txt
Note: I am executing the find command from the ABCD folder, In the results I want to exclude /home/ABCD/ folder I just want /test1/example1/sample1/testfile.txt as the result
How can I achieve this?
Since you are executing find from /home/ABCD/ do something like this:
find * -type f -print
Or if you are looking for files in test1 do this:
find test1 -type f -print
Also with -maxdepth N you can limit the recursion in find
If you only want to look for files named textfile.txt do
find test1 -type f -name 'textfile.txt' -print
If you want to print the leading slash do
find . -type f -printf '/%p\n'
For more info have a look here
Note: If have the above string in a variable, you can trim it like this:
string="/home/ABCD/test1/example1/sample1/textfile.txt"
echo "${string#/home/ABCD}"
Some more examples of string manipulation here
Just use . as the starting directory
find . -type f -print
gives
./test1/example1/sample1/textfile.txt
and if you really want a leading slash, use -printf for the output
find . -type f -printf '/%P\n'
You can use the mindepth parameter to start looking at one level below the current directory
find /home/ABCD/ -mindepth 1 -type f -print
This should substitute your current working directory name with a .
find . -type f | perl -pne "s#$PWD#.#"
So you would get results like:
./test1/example1/sample1/textfile.txt
If you do not want the preceeding ./, use this command instead:
find . -type f | perl -pne "s#$PWD/##"

Resources