Difference between these two commands (w & w/out "") and why? - linux

In linux, I have a file named test2 in my directory which I created using the touch command.
When I run the command
find . –name “*test*” -ls
It doesn't give me an error, but when I run
find . –name *test* -ls
It gave me an error
find: paths must precede expression: test2
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
Why is this?

*test* gets glob expanded by your shell (into more than one token).
Whereas no glob expansion happens in "*test*" because the surrounding " symbols prevent globbing.

Your shell is intercepting *test* and looking for files and directories in the current directory that match that expression, before it passes the expanded list to find. find expects only a single string in that spot, whereas the expanded list may be 0 or many strings.
With quotes, the shell ignores the asterisks and passes the raw string *test* to find, which then uses those asterisks as wildcards as you'd expect.

Related

Simple Bash Script that recursively searches in subdirs for a certain string

i recently started learning linux because a ctf contest is coming in the next months. The problem that I struggle with is that i am trying to make a bash script that starts from a directory, checks if the content is a directory or other kind of file. If it is a file,image etc apply strings $f | grep -i 'abcdef', if it is a directory cd to that directory and start over. i have c++ experience and i understand the logic but i can't really make it work.I can't succesfully implement the loop that goes thru all the subdirectories. All help would be appreciated!
you don not need a loop for this implementation. The find command can do what you are looking after.
for instance:
find /home -type f -exec sh -c " strings {} | grep abcd " \;
explain:
/home is you base directory can be anything
-type f: means a regular file
-exec from the man page:
"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."
If you want to just find the string in a file and you do not HAVE TO first find a directory and then a file and then search, you can just simply find the text with grep.
Go to the the parent directory and execute :
grep -iR "abcd"
Or from any place,
grep -iR "abcd" /var/log/mylogs/
Suggesting a grep command on find filter results:
grep "abcd" $(find . -type f)

Finding files of fixed length

I am trying to find file names that start with the letter 'a' and are of length 6. I have tried many variations, the latest one being:
find /usr/bin -type f -regex "^[a]" > grep {6}
However I get an error message of:
find: paths must precede expression: {6}
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
What am I doing wrong?
Without any regexes, just globbing:
find /usr/bin -type f -name 'a?????'
References:
Findutils manual: Shell pattern matching
Bash manual, Filename expansion and pattern matching
I would use the following command which is using extended posix regexes:
find /usr/bin -type f -regextype posix-extended -regex '.*/a.{5}'
Let me explain the pattern from the end:
.{5} matches five arbitrary characters
a matches a literal a
the / matches the path delimiter right before the filename
.* is the path, in this case /usr/bin
Btw, a simple command which does not even require a special regex engine would be:
find /usr/bin -type f -regex '.*/a.....'
$ is the end of the filename
..... are five arbitrary characters
a is a literal a
.*/ is the preceding path
Another thing. While your regex is wrong and grep is not required at all, why do you get this strange error message?
You are using find ... > grep where I think you wanted to use find ... | grep. Note that > will redirect the output of the find command to a file. In this case a file named grep. If you want to redirect the output of the find command into the input of a grep command you need to use the pipe symbol find ... | grep.
A > filename redirection can appear anywhere in a command line, it does not necessarily have to be at the end. That' why {6} is interpreted as the last argument to find. Since this argument is not expected, find supposed that you accidentally passed a search path at the end, which is a common mistake. That's why the message.

How to find files with specific pattern in directory with specific number? Linux

I've got folders named folder1 all the way up to folder150 and maybe beyond.. but I only want to find the complete path to text files in some of the folders (for example folder1 to folder50).
I thought a command like the following might work, but it is incorrect.
find '/path/to/directory/folder{1..50}' -name '*.txt'
The solution doesn't have to use find, as long as it does the correct thing.
find /path/to/directory/folder{1..50} -name '*.txt' 2>/dev/null
Or only basename
find /path/to/directory/folder{1..50} -name '*.txt' -exec basename {} \; 2>/dev/null
Or basename without .txt
find /path/to/directory/folder{1..50} -name '*.txt' -exec basename {} .txt \; 2>/dev/null
V. Michel's answer directly solves your problem; to complement it with an explanation:
Bash's brace expansion is only applied to unquoted strings; your solution attempt uses a single-quoted string, whose contents are by definition interpreted as literals.
Contrast the following two statements:
# WRONG:
# {...} inside a single-quoted (or double-quoted) string: interpreted as *literal*.
echo 'folder{1..3}' # -> 'folder{1..3}'
# OK:
# Unquoted use of {...} -> *brace expansion* is applied.
echo 'folder'{1..3} # -> 'folder1 folder2 folder 3'
Note how only the brace expression is left unquoted in the 2nd example above, which demonstrates that you can selectively mix quoted and unquoted substrings in Bash.
It is worth noting that it is - and can only be - Bash that performs brace expansion here, and find only sees the resulting, literal paths.[1]
find only accepts literal paths as filename operands.
(Some of find's primaries (tests), such as -name and -path, do support globs (as demonstrated in the question), but not brace expansion; to ensure that such globs are passed through intact to find, without premature expansion by Bash, they must be quoted; e.g., -name '*.txt')
[1] After Bash performs brace expansion, globbing (pathname expansion) may occur in addition, as demonstrated in ehaymore's answer; folder(?,[1-4]?,50) is brace-expanded to tokens folder?, folder[1-4]?, and folder50, the first two of which are subject to globbing, due to containing pattern metacharacters (?, [...]). Whether globbing is involved or not, the target program ultimately only sees the resulting literal paths.
You can give multiple directories to the find command, each matching part of the pattern you're looking for. For example,
find /path/to/directory/folder{?,[1-4]?,50} -name '*.txt'
which expands to three patterns:
folder? (matches 0-9)
folder[1-4]? (matches 10-49)
folder50
The question mark is a single-character wildcard.

Linux find command shell expansion

I have just a little question I don't understand with the find command.
I can do this :
[root#hostnaoem# ❯❯❯ls /proc/*/fd
But this give me an error :
[root#hostnaoem# ❯❯❯ find /proc/*/fd -ls
find: `/proc/*/fd': No such file or directory
even if I use "/proc//fd", /proc/""/fd or "/proc/*/fd"
I've searched wha find shell expansion says about that, but I found nothing. Can someone tell me why?
Thanks
If you just RTFM, you'll learn that the syntax for find is:
find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]
The usually used subset of that is:
find whereToSearch (-howToSearch arg)*
To find all files|directories named fd in /proc:
find /proc -name fd
-name is the most common howToSearch expression:
-name pattern
Base of file name (the path with the leading directories
removed) matches shell pattern pattern. The metacharacters
(`*', `?', and `[]') match a `.' at the start of the base name
(this is a change in findutils-4.2.2; see section STANDARDS CON‐
FORMANCE below). To ignore a directory and the files under it,
use -prune; see an example in the description of -path. Braces
are not recognised as being special, despite the fact that some
shells including Bash imbue braces with a special meaning in
shell patterns. The filename matching is performed with the use
of the fnmatch(3) library function. Don't forget to enclose
the pattern in quotes in order to protect it from expansion by
the shell.
(Note the the last sentence)
If your pattern contains slashes, you need -path or -wholename (same thing):
find /proc/ -wholename '/proc/[0-9]*/fd' 2>/dev/null
Other expressions you might want to use are:
-type
-depth, -mindepth, -maxdepth
-user, -uid
See find(1) to learn more about each search expressions. If you want to search the in-terminal manual (man find or man 1 find), you can use the / character to enter search mode (like Ctrl+F in most GUI apps).
Usage of ls with globbing (*) is generally a code smell. Unless you use the -d flag, it'll list the contents of the directories that match the glob pattern in addition to the matches.
I find the echo globpattern form generally more convenient for viewing the results of a glob pattern match.
This work :
[root#hostname # ❯❯❯ find /proc/ -path /proc/*/fd -ls
Regards.

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).

Resources