Linux: how to look for files with a certain extension in hierarchy and execute command whenever one is found? - linux

I have a directory hierarchy, whose names do not follow a pattern. E.g.
parent
bcgegec
hfiwehfiuwe
huiwwuifegeufg
whegwgefyfeg
hfeohfeiofe
chidchuehugfe
dedewdewf
tegtgetg
gtgetgtg
and so on.
Inside some of such directories there is a file with "gr" extension. I need to find each of such files, cd to its dir and execute "gnuplot" command having the .gr file as argument. I tried the following to nest two find commands, but the {} of the inner one does not work as I need. The outer find should iterate for every directory, and the inner find should look for the presence of the .gr file.
find $parentDir -type d -exec sh -c '(cd {} && find . -maxdepth 1 -name *.gr -exec /usr/bin/gnuplot {} \;)' \;

Perhaps this is what you are looking for:
find . -type f -name "*.gr" -execdir /usr/bin/gnuplot {} \;
Read through man find for other useful information.

Related

Linux move files from dir to dir by name mask

I am trying to move all files with names starts with SML from directory to another.
Tried with
find /var/.../Images/ -name SML\* mv /var/.../Images/Small but doesnt work
try find /var/.../Images/ -name SML\* -exec mv {} /var/.../Images/Small/ \;
I guess you want something like this:
dir=/path/to/your/Images
mkdir -p "$dir/Small"
find "$dir" -name "SML*" -not -wholename "$dir/Small/*" -exec mv {} "$dir/Small/" \;
Since the directory you move the files to is a subdirectory of the one you seach in, you need to exclude the files already moved there. So I added -not -wholename "$dir/Small/*"
To execute a command for each found file, you need -exec .... The alternative would be to pipe your find results to a while read loop.
When using -exec, the found name can be referenced by {}.
See man find for a lot more information.

How to rename files in different directories with the same name using find

I have files named test.txt in different directories like this
./222/test.txt
./111/test.txt
I want to rename all test.txt to info.txt
I've tried using this
find . -type f -iname 'test.txt' -exec mv {} {}info \;
I get test.txtinfo
Your idea is right, but you need to use -execdir instead of just -exec to simplify this.
find . -type f -iname 'test.txt' -execdir mv {} info.txt ';'
This works like -exec with the difference that the given shell command is executed with the directory of the found pathname as its current working directory and that {} will contain the basename of the found pathname without its path. Also note that the option is a non-standard one (non POSIX compliant).

Linux Find Command Hide Base Directory

Editor's note: This question is ambiguous, because it conflates two unrelated tasks:
(a) to print mere filenames (without path components) using the -printf action, and
(b) to pass the mere filename as an argument in the context of an -exec action via {}
(a) is mistakenly perceived as a way to implement (b).
This confusion has led to at least one answer focusing on (a).
I'm trying to use the find command to list all directories in a certain path, but hide that path in the output. The -printf "%P\n" flag is supposed to hide /path/to/directory/, but it's not working:
find /path/to/directory/* -maxdepth 0 -type d -printf "%P\n" -exec sudo tar -zcpvf {}.tar.gz {} \;
For example, the above command would create archives with:
/path/to/directory/dir1
/path/to/directory/dir2
/path/to/directory/dir3
How can I modify my command to output this:
dir1
dir2
dir3
Please note: I know I can do the above by cd /path/to/directory/ then using the find command, but it's important that I avoid using cd and do it all with the single find command.
find /path/to/directory/* -maxdepth 0 -type d -exec basename {} \;
find all directories find /path/to/directory/* -maxdepth 0 -type d
-exec basename {} \; - execute basename command with result parameters from find
Since you're only processing child directories (immediate subdirectories), a shell loop may be the simpler solution:
(cd "/path/to/dir" && for d in */; do sudo tar -zcpvf "${d%/}".tar.gz "$d"; done)
I know you want to avoid cd, but by enclosing the entire command in (…), it is run in a subshell, so the current shell's working dir. remains unchanged.
The remainder of this answer discusses how to solve the problem using GNU find.
The -execdir solution would work with BSD/OSX find too, and would actually be simpler there.
As for getting find's -printf to only print the filenames, without any directory components: use the %f format specifier:
find /path/to/dir/* -maxdepth 0 -type d -printf "%f\n"
This will print the mere names of all immediate subdirectories in the specified directory.
However, what you print has no effect on what {} expands to when using the -exec action: {} always expands to the path as matched, which invariably includes the path component specified as the input.
However, the -execdir action may give you what you want:
it changes to the directory at hand before executing the specified command
it expands {} to ./<filename> - i.e., the mere filename (directory name, in this case), prefixed with ./ - and passes that to the specified command.
Thus:
find /path/to/dir -mindepth 1 -maxdepth 1 -type d -execdir sudo tar -zcpvf {}.tar.gz {} \;
Caveat: -execdir only behaves as described for files that are descendants of the input paths; for the input paths themselves, curiously, {} still expands to the input paths as-is[1]
.
Thus, the command above does not use globbing (/path/to/dir/*) with -maxdepth 0, but instead uses /path/to/dir and lets find do the enumeration of contained items, which are than at level 1 - hence -maxdepth 1; since the input path itself should then not be included, -mindepth 1 must be added.
Note that the behavior is then subtly different: find always includes hidden items in the enumeration, whereas the shell's globbing (*) by default does not.
If the ./ prefix in the {} expansions should be stripped, more work is needed:
find /path/to/dir -mindepth 1 -maxdepth 1 -type d \
-execdir sh -c 'd=${1##*/}; sudo tar -zcpvf "$d".tar.gz "$d"' - {} \;
Involving the shell (sh) allows stripping the ./ prefix using a shell parameter expansion (${1##*/} would, in fact, strip any path component).
Note the dummy argument -, which the shell assigns to $0, which we're not interested in here; {} then becomes shell parameter $1.
[1] With ./ prepended, if an input path is itself relative; note that BSD/OSX find does not exhibit this quirk: it always expands {} to the mere filename, without any path component.

Bash Command for Finding the size of all files with particular filetype in a directory in ubuntu

I have a folder which contains several file types say .html,.php,.txt etc.. and it has sub folders also .Sub folders may contain all the file types mentioned above.
Question1:- I want to find size of all the files having the file type as '.html' which are there in both root directory and in sub- directories
Question2:- I want to find size of all the files having the file type as '.html' which are there only in root directory but not in sub folders.
I surfed through the internet but all i am able to get is commands like df -h, du -sh etc..
Are there any bash commands for the above questions? Any bash scripts?
You can use the find command for that.
#### Find the files recursively
find . -type f -iname "*.html"
#### Find the files on the r
find . -type f -maxdepth 1 -iname "*.iml"
Then, in order to get their size, you can use the -exec option like this:
find . -type f -iname "*.html" -exec ls -lha {} \;
And if you really only need the file size (I mean, without all the other stuff that ls prints):
find . -type f -iname "*.html" -exec stat -c "%s" {} \;
Explanation:
iname search of files without being case sensitive
maxdepth travels subdirectories recursively up to the specify level (1 means only the immediate folder)
exec executes an arbitrary command using the found paths, where "{}" represents the path of the file
type indicates the type of file (a directory is a file in Linux)

Copy specific files recursively

This problem has been discussed extensively but I couldn't find a solution that would help me.
I'm trying to selectively copy files from a directory tree into a specific folder. After reading some Q&A, here's what I tried:
cp `find . -name "*.pdf" -type f` ../collect/
I am in the right parent directory and there indeed is a collect directory a level above. Now I'm getting the error: cp: invalid option -- 'o'
What is going wrong?
To handle difficult file names:
find . -name "*.pdf" -type f -exec cp {} ../collect/ \;
By default, find will print the file names that it finds. If one uses the -exec option, it will instead pass the file names on to a command of your choosing, in this case a cp command which is written as:
cp {} ../collect/ \;
The {} tells find where to insert the file name. The end of the command given to -exec is marked by a semicolon. Normally, the shell would eat the semicolon. So, we escape the semicolon with a backslash so that it is passed as an argument to the find command.
Because find gives the file name to cp directly without interference from the shell, this approach works for even the most difficult file names.
More efficiency
The above runs cp on every file found. If there are many files, that would be a lot of processes started. If one has GNU tools, that can be avoided as follows:
find . -name '*.pdf' -type f -exec cp -t ../collect {} +
In this variant of the command, find will supply many file names for each single invocation of cp, potentially greatly reducing the number of processes that need to be started.

Resources