Get n level parent with find command - linux

I want to find my n-th level parent with the find command,
initially, when used this command, it gives me the whole file path:
Modified_files_users="$(find /var/lib/abcccc/tamm/acb-Beta-DB-abcc/abc-central/src/main/taff/com/hifinite/components/user
-type f -mtime -5;)";
Output:
/var/lib/abcccc/tamm/acb-Beta-DB-abcc/abc-central/src/main/taff/com/hifinite/components/user/file/foo.ext
Hence I used the basename GNU with find, but it only gives the file name.
Modified_files_users="$(find /var/lib/abcccc/tamm/acb-Beta-DB-abcc/abc-central/src/main/taff/com/hifinite/components/user
-type f -mtime -5 -exec basename \{} \;)";
Ouput:
foo.txt
but the Output I expect is
/file/foo.ext
Is there any way I can get this by adding anything to the -exec command?
basically either I should be able to specify the nth parent which should be included in the output OR) find the whole path after
/var/lib/abcccc/tamm/acb-Beta-DB-abcc/abc-central/src/main/taff/com/hifinite/components/user

You need to use printf with %P:
find somedirectory -type f -printf '%P\n'
Document:
%P File’s name with the name of the command line argument under which it was found removed.
Example:
$ find /home/abc/temp -type f
/home/abc/temp/A2018001.txt
/home/abc/temp/myfiles.zip
/home/abc/temp/org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class
With printf %P:
$ find /home/abc/temp -type f -printf '%P\n'
A2018001.txt
myfiles.zip
org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class

Related

Using 'find' to return filenames without extension

I have a directory (with subdirectories), of which I want to find all files that have a ".ipynb" extension. But I want the 'find' command to just return me these filenames without the extension.
I know the first part:
find . -type f -iname "*.ipynb" -print
But how do I then get the names without the "ipynb" extension?
Any replies greatly appreciated...
To return only filenames without the extension, try:
find . -type f -iname "*.ipynb" -execdir sh -c 'printf "%s\n" "${0%.*}"' {} ';'
or (omitting -type f from now on):
find "$PWD" -iname "*.ipynb" -execdir basename {} .ipynb ';'
or:
find . -iname "*.ipynb" -exec basename {} .ipynb ';'
or:
find . -iname "*.ipynb" | sed "s/.*\///; s/\.ipynb//"
however invoking basename on each file can be inefficient, so #CharlesDuffy suggestion is:
find . -iname '*.ipynb' -exec bash -c 'printf "%s\n" "${#%.*}"' _ {} +
or:
find . -iname '*.ipynb' -execdir basename -s '.sh' {} +
Using + means that we're passing multiple files to each bash instance, so if the whole list fits into a single command line, we call bash only once.
To print full path and filename (without extension) in the same line, try:
find . -iname "*.ipynb" -exec sh -c 'printf "%s\n" "${0%.*}"' {} ';'
or:
find "$PWD" -iname "*.ipynb" -print | grep -o "[^\.]\+"
To print full path and filename on separate lines:
find "$PWD" -iname "*.ipynb" -exec dirname "{}" ';' -exec basename "{}" .ipynb ';'
Here's a simple solution:
find . -type f -iname "*.ipynb" | sed 's/\.ipynb$//1'
I found this in a bash oneliner that simplifies the process without using find
for n in *.ipynb; do echo "${n%.ipynb}"; done
If you need to have the name with directory but without the extension :
find . -type f -iname "*.ipynb" -exec sh -c 'f=$(basename $1 .ipynb);d=$(dirname $1);echo "$d/$f"' sh {} \;
find . -type f -iname "*.ipynb" | grep -oP '.*(?=[.])'
The -o flag outputs only the matched part. The -P flag matches according to Perl regular expressions. This is necessary to make the lookahead (?=[.]) work.
Perl One Liner
what you want
find . | perl -a -F/ -lne 'print $F[-1] if /.*.ipynb/g'
Then not your code
what you do not want
find . | perl -a -F/ -lne 'print $F[-1] if !/.*.ipynb/g'
NOTE
In Perl you need to put extra .. So your pattern would be .*.ipynb
If there's no occurrence of this ".ipynb" string on any file name other than a suffix, then you can try this simpler way using tr:
find . -type f -iname "*.ipynb" -print | tr -d ".ipbyn"
If you don't know that the extension is or there are multiple you could use this:
find . -type f -exec basename {} \;|perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
and for a list of files with no duplicates (originally differing in path or extension)
find . -type f -exec basename {} \;|perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'|sort|uniq
Another easy way which uses basename is:
find . -type f -iname '*.ipynb' -exec basename -s '.ipynb' {} +
Using + will reduce the number of invocations of the command (manpage):
-exec command {} +
This variant of the -exec action runs the specified command on
the selected files, but the command line is built by appending
each selected file name at the end; the total number of
invocations of the command will be much less than the number
of matched files. The command line is built in much the same
way that xargs builds its command lines. Only one instance of
'{}' is allowed within the command, and (when find is being
invoked from a shell) it should be quoted (for example, '{}')
to protect it from interpretation by shells. The command is
executed in the starting directory. If any invocation with
the `+' form returns a non-zero value as exit status, then
find returns a non-zero exit status. If find encounters an
error, this can sometimes cause an immediate exit, so some
pending commands may not be run at all. For this reason -exec
my-command ... {} + -quit may not result in my-command
actually being run. This variant of -exec always returns
true.
Using -s with basename runs accepts multiple filenames and removes a specified suffix (manpage):
-a, --multiple
support multiple arguments and treat each as a NAME
-s, --suffix=SUFFIX
remove a trailing SUFFIX; implies -a

Inserting text in a 'find' command search

I have a find string that finds all the instances of a particular filename in a path, like so:
find /opt/logs* -type f -name "deploy.log" -exec ls {} \;
I need to return the result with 'FINENAME=' prepended on each line. Having a hard time figuring the best way.
find /opt/logs* -type f -name deploy.log | sed 's/^/FILENAME=/'
Note that if you have a directory named /opt/logs (and you're not trying to look in /opt/logs-foo/ and /opt/logs-date, or the like) you can drop the * and write find /opt/logs -type ...
Use the -printf option:
find /opt/logs* -type f -name "deploy.log" -printf='FILENAME=%p\n'
%p will get expanded to the file's name.

Skip characters in "find" output

I'm writing a bash script which in a certain part of the process should list the files in a directory older than 1 day and print the list to a text file to work with it later. This is the current command I have:
find . -mtime +0 > list.txt
The problem with this command is that it prints the filenames preceded by "./", e.g.:
./file1
./file2
./file3
How can I do to print only the filenames in this way?
file1
file2
file3
Use basename:
find . -mtime +0 -type f -exec basename {} \; > list.txt
(the reason for the -type f is because otherwise the searched directory is printed).
No need to use extra binary commands if your find supports it:
find . -mtime +0 -printf '%f\n' > list.txt
When targeting files, just add -type f:
find . -mtime +0 -printf '%f\n' -type f > list.txt
Or if you intend to show the files and directories in a specified directory:
find some_dir -mtime +0 -printf '%f\n' -mindepth 1 > list.txt

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/##"

In Unix,cmd to search a file recursively and retrieve the file instead of just the path of the file

In Unix, what is the single cmd that lets me search and locate a file recursively and then retrieve the file instead of just the path of the file?
What do you mean by retrieve?
You can simply use -exec argument to find.
$ find /path/to/search -type f -name '*.txt' -exec cat {} \;
$ find /path/to/search -type f -name 'pattern' -exec cp {} /path/to/new \;
The second one should work.
cat `find /wherever/you/want/to/start/from -name name_of_file`
Note those quotes are backquotes (`).

Resources