find command in bash shell and the -name option doubts - bash

What is the difference between the two below:
find . -type f -name \*.bmp
find . -type f -name *.bmp
I have tested,they both return the same result,so is there anything different _deep inside_?
Added from the removed answer:
So it is to avoid the shell expansion for the special ***** character,solely pass * as a argument to the find command and let it process it.
But on my machine,they are all good, both return the bmp files in and below the current directory,to name a few,the result is like below,some are omitted for brevity
./images/building_color.bmp
./images/building_gray.bmp
./images/car_gray.bmp
./images/temple_color.bmp
./images/boat_gray.bmp
./images/tools_gray.bmp
./images/temple_gray.bmp
./images/tools_color.bmp
./images/car_color.bmp
./images/boat_color.bmp
system info:
GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu)
Linux sysabod-laptop 2.6.32-30-generic #59-Ubuntu SMP Tue Mar 1 21:30:21 UTC 2011 i686 GNU/Linux

Here's how they're different: the first one always works, and the second one doesn't.
As for why: in bash, shell globs (wildcard patterns including * or ?) are expanded by the shell into all files matching the glob. However, if no such files exist, the pattern is left alone.
So, if you're in a directory with no bmp files, the commands work the same way, because the first is escaped and bash fails to find any files matching in the second case.
If you ran it from a directory containing only one such file, say foo.bmp, the first would find all bmp files in the subtree, while the second would find all files named foo.bmp only. If run in a directory with multiple bmp files, I believe you'll get an error because find doesn't know what to do with all the filenames.

When you escape the asterisk (\*) the asterisk itself is passed as argument to the find command and will be evaluated by find. If you don't escape the asterisk (*) already the shell evaluates it and expands it to the file names matching the pattern.
Fore example consider following directory structure:
./a.txt
./b.bmp
./c.bmp
./dir/d.doc
./dir/e.bmp
When you execute
find . -type f -name *.bmp
the shell expands *.bmp to b.bmp c.bmp. I.e. the command that is actually executed will be:
find . -type f -name b.bmp c.bmp
which will find b.bmp and c.bmp but not dir/e.bmp.
When you execute
find . -type f -name \*.bmp
*.bmp is passed directly as it is to find. find will recurse through the current directory (.) and all its subdirectories (in the example only dir) and will find all files in those directories matching the pattern. The result will be: b.bmp, c.bmp and also dir/e.bmp.

The first command:
find . -type f -name \*.bmp
passes an asterisk to the find command, and that tells it to find all the files in and below the current directory ending with .bmp.
The second command:
find . -type f -name *.bmp
may be resolved by the shell to, for example:
find . -type f -name image1.bmp image2.bmp image3.bmp
(that would be the bmp files in the current directory only)
and find would only list them, not the bmp files in other directories below the current one.

Related

executable files in linux using (perm)?

i'm trying to write out a list of the names of everything under the /etc directory that are executable to all other users and whose name starts or ends with a number.
find /etc "(" -name "[0-9]*" -o -name "*[0-9]" ")" -perm -o=x -print
But every time I get a wrong answer, can you help?
If you're using the zsh shell, you can get that list of files with its advanced filename generation globbing; no external programs needed. In particular, using a recursive glob, alternation, and a glob qualifier that matches world-executable files:
zsh$ printf "%s\n" /etc/**/([0-9]*|*[0-9])(X)
/etc/alternatives/animate-im6
/etc/alternatives/c89
/etc/alternatives/c99
/etc/alternatives/compare-im6
/etc/alternatives/composite-im6
...
/etc/X11
/etc/X11/fonts/Type1
/etc/xdg/xdg-xubuntu/xfce4
/etc/xdg/xfce4
/etc/xfce4
Do a setopt glob_dots first to match filenames starting with . like find does. Otherwise they get skipped.
If you're using find, you need the -mode argument to -perm to select files with at least the given permission bits (Which is actually what you have in your question and works for me)
find /etc \( -name "[0-9]*" -o -name "*[0-9]" \) -perm -o=x

Find and rename multiple files using a bash script in Linux

As an example, in a directory /home/hel/files/ are thousends of files and hundreds of directories.
An application saves there its output files with special characters in the file names.
I want to replace these special characters with underscores in all file names. e.g. -:"<>#
I wrote a bash script which simply repeats a command to rename the files using Linux/Unix 'rename'.
Example: file name: rename.sh
#!/bin/bash
rename "s/\'/_/g" *
rename 's/[-:"<>#\,&\s\(\)\[\]?!–~%„“;│\´\’\+#]/_/g' *
rename 'y/A-Z/a-z/' *
rename 's/\.(?=[^.]*\.)/_/g' *
rename 's/[_]{2,}/_/g' *
I execute the following find command:
find /home/hel/files/ -maxdepth 1 -type f -execdir /home/hel/scripts/rename.sh {} \+
Now the issue:
This works fine, except the fact, that it renames subdirectories too, if they have the searched characters in their name.
The find command searches just for files and not for directories.
I tried some other find variations like:
find /home/hel/files/ -maxdepth 1 -type f -execdir sh /home/hel/scripts/rename.sh {} \+
find /home/hel/files/ -maxdepth 1 -type f -execdir sh /home/hel/scripts/rename.sh {} +
find /home/hel/files/ -maxdepth 1 -type f -execdir sh /home/hel/scripts/rename.sh {} \;
They are all working, but with the same result.
What is not working:
find /home/hel/files/ -maxdepth 1 -type f -exec sh /home/hel/scripts/rename.sh {} \+
This one is dangerous, because it renames the directories and files in the current directory, where you call the find command too.
Maybe one has an idea, why this happens or has a better solution.
The script rename.sh did not use its command line arguments at all, but instead searched files and directories (!) on its own using the glob *.
Change your script to the following.
#!/bin/bash
rename -d s/\''/_/g;
s/[-:"<>#\,&\s\(\)\[\]?!–~%„“;│\´\’\+#]/_/g;
y/A-Z/a-z/;
s/\.(?=[^.]*\.)/_/g;
s/[_]{2,}/_/g' "$#"
Then use find ... -maxdepth 1 -type f -exec sh .../rename.sh {} +.
Changes Made
Use "$#" instead of * to process the files given as arguments rather than everything in the current directory.
Execute rename only once as a 2nd rename wouldn't find the files specified with "$#" after they were renamed by the 1st rename.
Use the -d option such that only the basenames are modified. find always puts a path in front of the files, at the very least ./. Without this option rename would change ./filename to mangledPath/newFilename and therefore move the file to another directory.
Note that man rename is a bit misleading
--path, --fullpath
Rename full path: including any directory component. DEFAULT
-d, --filename, --nopath, --nofullpath
Do not rename directory: only rename filename component of path.
For a given path rename -d 's...' some/path/basename just processes the basename and ignores the leading components some/path/. If basename is a directory it will still be renamed despite the -d option.

get a list of files and directories with full path in unix

I am trying to get full path of both the files and directories from a directory. I tried using find but unable to get result.
when I used find /home/demo -type f it only lists files and find /home/demo -type d only lists directories.
Is there a way to get both using Find?
You can specify the absolute path of a directory. As an example for the current directory:
find "`pwd`"
pwd shows full path of current directory. ` ` summons a subshell in which output can be used as an argument to the command.
A literal example can be:
find /home/user
Update: You can use -o to explicitly target both files and directories. Doing find without an option may include other types besides the two.
find /home/user \( -type f -o -type d \)
Note: -or is synonymous but may not work in other versions of find since it's not POSIX compliant.

Terminal : Difference between quoted argument and non quoted in find function

What is the difference between these two commands in the command line in the terminal on ubuntu?
find . -name "*.txt"
find . -name *.txt
It depends on the shell you are using and the contents of your directory.
If you're unfortunate enough to use csh (or some of its heritage, like zsh) for some reason, it will refuse to execute this when there is no file in your directory that matches the pattern (unless you've turned that behaviour off).
If there are files that match the pattern, the command will be expanded by the shell:
find . -name foo.txt bar.txt baz.txt
and find will report a syntax error. Unless there is a single file that matches, then you get:
find . -name foo.txt
and this will then only find files named "foo.txt" in subdirectories.
Only if you're using a sane shell and there are no files matching in the current directory will the two commands be identical.
Moral: don't do it. Always use quotes.

What does this bash script means

I've found the following line of code in a script. Could someone explain me what does this following line of code means?
Basically, the purpose of this line is find a set of files to archive. Since I am not familiar with bash scripts, it is difficult for me to understand this line of code.
_filelist=`cd ${_path}; find . -type f -mtime ${ARCHIVE_DELAY} -name "${_filename}" -not -name "${_ignore_filename}" -not -name "${_ignore_filename2}"`
Let's break it down:
cd ${_path} : changes to the directory stored in the ${_path} variable
find is used to find files based on the following criteria:
. : look in the current directory and recurse through all
sub-directories
-type f: look for regular files only (not directories)
-mtime ${ARCHIVE_DELAY} : look for files last modified
${ARCHIVE_DELAY}*24 hours ago
-name "${_filename}": look for files which have name matching ${_filename}
-not -name "${_ignore_filename}" : do not find files which have
name matching ${_ignore_filename}
-not -name "${_ignore_filename2}" : do not find files which have
name matching ${_ignore_filename2}
All the files found are stored in a variable called _filelist.
The backtick (`) symbol assigns to the variable the output of the command.
Your script is assigning to $_filelist what you get by:
Changing directory to $_path
Finding in the current directory (.) files (-type f) where
Name is $_filename (a pattern, I suppose)
Name is not $_ignore_filename or $_ignore_filename2
I think you could as well change that to find ${_path} ... without the cd, but please try it out.
_filelist=`somecode`
makes the variable _filelist contain the output of the command somecode.
Somecode, in this case, is mostly a find command, which searches recursively for files.
find . -type f -mtime ${ARCHIVE_DELAY} -name "${_filename}" -not -name "${_ignore_filename}" -not -name "${_ignore_filename2}"
find .
searches the current dir, but this was just before changed to be _path.
-type f
only searches in ordinary files (not dirs, sockets, ...)
-mtime
specifies the modification time of that files, to be the same as ${ARCHIVE_DELAY}
-name explains
itself, has to be "${_filename}"
-not name
explains itself too, I guess.
So the whole part sets the variable filelist to files, found by some criterias: name, age, and type.

Resources