Exclude specific path from find's output - shell

I searched inside my filesystem for files with a specific extension using find
find / -type f -name "*.click" 2>/dev/null
In my result I get a lot of files in a specific path which are not interesting for me like
/home/x/Dokumente/click/etc/samplepackage/test.click
/home/x/Dokumente/click/apps/csclient/test.click
/home/x/Dokumente/click/conf/test-device.click
/home/x/Dokumente/click/conf/ip6ndadvertiser.click
/home/x/Dokumente/click/conf/script-parabolawave.click
/home/x/Dokumente/click/conf/fake-iprouter.click
/home/x/Dokumente/click/conf/fromhost-tunnel.click
/home/x/Dokumente/click/conf/simple-dsdv-userlevel.click
/home/x/Dokumente/click/conf/sampler.click
/home/x/Dokumente/click/conf/script-trianglewave.click
/home/x/Dokumente/click/conf/script-squarewave.click
/home/x/Dokumente/click/conf/fastudpsrc.click
/home/x/Dokumente/click/conf/schedorder1.click
/home/x/Dokumente/click/conf/test-ping-userlevel.click
/home/x/Dokumente/click/conf/test-tcp.click
/home/x/Dokumente/click/conf/delay.click
/home/x/Dokumente/click/conf/icmp6error.click
/home/x/Dokumente/click/conf/webgen.click
/home/x/Dokumente/click/conf/test2.click
/home/x/Dokumente/click/conf/grid.click
/home/x/Dokumente/click/conf/thomer-nat.click
/home/x/Dokumente/click/conf/gnat02.click
/home/x/Dokumente/click/conf/test-clicky.click
/home/x/Dokumente/click/conf/ip64-nat3.click
/home/x/Dokumente/click/conf/mazu-nat.click
/home/x/Dokumente/click/conf/ip6print.click
/home/x/Dokumente/click/conf/gnat01.click
/home/x/Dokumente/click/conf/ip601.click
In this article I found an answer and tried like this below
find / -type f -name "*.click" -not -path "./home/ipg7/Dokumente/click/conf" 2>/dev/null
But it doesn't work I still get the output as before.
So how can I exclude a specific path and also write all the permission denied to /dev/null?

You've added -not -path "./home/ipg7/Dokumente/click/conf" which does not match anything in your output due to
starting with a dot,
missing the pattern that matches the file part
Try -not -path "/home/ipg7/Dokumente/click/conf/*" instead

You have to use globbing in path to ignore all matches from a given path and use 2>/dev/null to ignore error:
find / -type f -name "*.click" -not -path "./home/ipg7/Dokumente/click/conf/*" 2>/dev/null

Related

Why can't I exclude a directory using find

I attempting to run a command on all subdirectories in a directory using find and -exec, however on one of the directories, the user the script runs under does not have adequate permissions and I get an error (permission denied). I am attempting to ignore the directory using either ! -path or using -prune. Neither of these methods work. I have tried both of the commands down below.
I have tried every combination of subDirToExclude— with and without ./ at the beginning, with and without /* at the end. I've tried relative path, full path and every single combination of all of them that you can think of to try and match this path, but it simply does not work. The man page is unhelpful and no suggestions from any related questions on this forum produce any useful results. Why do none of the methods suggested in the man page work? How can this actually be done?
find /path/to/dir -maxdepth 1 -type d ! -path "subDirToExclude" -exec somecommand {} +
find /path/to/dir -maxdepth 1 -type d -path "subDirToExclude" -prune -o -exec somecommand {} +
find: ‘/path/to/dir/subDirToExclude’: Permission denied
The argument to the -path option should be a full pathname, not just the name of the directory. Use -name if you just want to match the name of the directory.
find /path/to/dir -maxdepth 1 -type d ! -name "subDirToExclude" -exec somecommand {} +
You could also do this without using find at all, since you're not recursing into subdirectories because of -maxdepth 1.
shopt -s extglob
somecommand /path/to/dir /path/to/dir/!(subDirToExclude)/
Putting / at the end of the filename makes the wildcard only match directories. Actually, this will also match symbolic links to directories; if that's a problem, you can't use this solution.

Why is my `find` command giving me errors relating to ignored directories?

I have this find command:
find . -type f -not -path '**/.git/**' -not -path '**/node_modules/**' | xargs sed -i '' s/typescript-library-skeleton/xxx/g;
for some reason it's giving me these warnings/errors:
find: ./.git/objects/3c: No such file or directory
find: ./.git/objects/3f: No such file or directory
find: ./.git/objects/41: No such file or directory
I even tried using:
-not -path '**/.git/objects/**'
and got the same thing. Anybody know why the find is searching in the .git directory? Seems weird.
why is the find searching in the .git directory?
GNU find is clever and supports several optimizations over a naive implementation:
It can flip the order of -size +512b -name '*.txt' and check the name first, because querying the size will require a second syscall.
It can count the hard links of a directory to determine the number of subdirectories, and when it's seen all it no longers needs to check them for -type d or for recursing.
It can even rewrite (-B -or -C) -and -A so that if the checks are equally costly and free of side effects, the -A will be evaluated first, hoping to reject the file after 1 test instead of 2.
However, it is not yet clever enough to realize that -not -path '*/.git/*' means that if you find a directory .git then you don't even need to recurse into it because all files inside will fail to match.
Instead, it dutifully recurses, finds each file and matches it against the pattern as if it was a black box.
To explicitly tell it to skip a directory entirely, you can instead use -prune. See How to exclude a directory in find . command
Both more efficient and more correct would be to avoid the default -print action, change -not -path ... to -prune, and ensure that xargs is only used with NUL-delimited input:
find . -name .git -prune -o \
-name node_modules -prune -o \
-type f -print0 | xargs -0 sed -i '' s/typescript-library-skeleton/xxx/g '{}' +
Note the following points:
We use -prune to tell find to not even recurse down the undesired directories, rather than -not -path ... to tell it to discard names in those directories after they were found.
We put the -prunes before the -type f, so we're able to match directories for pruning.
We have an explicit action, not depending on the default -print. This is important because the default -print effectively has a set of parenthesis: find ... behaves like find '(' ... ')' -print, not like find ... -print, no if explicit action is given.
We use xargs only with the -0 argument enabling NUL-delimited input, and the -print0 action on the find side to generate a NUL-delimited list of names. NUL is the only character which cannot be present in an arbitrary file path (yes, newlines can be present) -- and thus the only character which is safe to use to separate paths. (If the -0 extension to xargs and the -print0 extension to find are not guaranteed to be available, use -exec sed -i '' ... {} + instead).

find files and exclude some files and a directory

I want to find in a directory all files with extension .hs but exclude all files in a sub-directory sub and some other files with names containing test.
I read and experimented with the use of find and prune but did not understand the complex logic and none of my attempts worked.
The naive
find . -name "*.hs" -not -name '*sub*' -not -name "*test*"
nor
find . -name "*.hs" -not -path '/sub' -not -name "*test*"
does work. I assume there should be a simple solution to this (relatively) simple issue.
A solution that seems to work is
find . -name "*.hs" -not -name "*test*" | grep -v "sub"
which is simpler than using prune, but can certainly be improved?
Your first attempt excludes all files whose name includes sub.
Your second attempt excludes all files whose path is exactly /sub.
Combine the two to match all files whose path includes sub:
-not -path "*sub*"
However, -prune is the better solution because it skips the directory rather than fruitlessly matching every single entry in it.

In Bash, how do you delete all files with same name, except the one located in a specific folder?

I have a specific file which is found in several directories. Usually I delete all of them by using the syntax:
find . -name "<Filename>" -delete
However, I want to retain one file from a specific folder, say FOLDER1.
How do I do this using find? (I want to use find because I use -print before -delete to check what files I am deleting. I am apprehensive on using rm since there is danger of deleting files I want to keep.)
Thanks in advance.
You can do it with
find . -name "filename" -and -not -path "./path/to/filename" -delete
You will want either to make sure that the path expression is a relative one, including the initial ./, so that it's matched by the expression, or else use wildcards. So if you know that it's in a folder named myfolder, but you don't know the full path to it, you can use
find . -name "filename" -and -not -path "*/myfolder/filename" -delete
If you don't want to delete anything under any directory named FOLDER1, you can tell find not to recurse down any directory so named at all, using -prune:
find . -name FOLDER1 -prune -o -name filename -delete
This is more efficient than recursing down that directory and then filtering out results that include it later.
Side note: When testing this, be sure you use the explicit -print:
find . -name FOLDER1 -prune -o -name filename -print
...whereas an implicit one won't behave as you expect:
# not what you want: equivalent to the below, not the above:
find . -name FOLDER1 -prune -o -name filename
...will behave as:
find . '(' -name FOLDER1 -prune -o -name filename ')' -print
...which thus includes contents on either side of the -o operator for the action.

Exclude a sub-directory using find

I have directory structure like this
data
|___
|
abc
|____incoming
def
|____incoming
|____processed
123
|___incoming
456
|___incoming
|___processed
There is an incoming sub-folder in all of the folders inside Data directory. I want to get all files from all the folders and sub-folders except the def/incoming and 456/incoming dirs.
I tried out with following command
find /home/feeds/data -type d \( -name 'def/incoming' -o -name '456/incoming' -o -name arkona \) -prune -o -name '*.*' -print
but it is not working as expected.
Ravi
This works:
find /home/feeds/data -type f -not -path "*def/incoming*" -not -path "*456/incoming*"
Explanation:
find /home/feeds/data: start finding recursively from specified path
-type f: find files only
-not -path "*def/incoming*": don't include anything with def/incoming as part of its path
-not -path "*456/incoming*": don't include anything with 456/incoming as part of its path
Just for the sake of documentation: You might have to dig deeper as there are many search'n'skip constellations (like I had to). It might turn out that prune is your friend while -not -path won't do what you expect.
So this is a valuable example of 15 find examples that exclude directories:
http://www.theunixschool.com/2012/07/find-command-15-examples-to-exclude.html
To link to the initial question, excluding finally worked for me like this:
find . -regex-type posix-extended -regex ".*def/incoming.*|.*456/incoming.*" -prune -o -print
Then, if you wish to find one file and still exclude pathes, just add | grep myFile.txt.
It may depend also on your find version. I see:
$ find -version
GNU find version 4.2.27
Features enabled: D_TYPE O_NOFOLLOW(enabled) LEAF_OPTIMISATION SELINUX
-name only matches the filename, not the whole path. You want to use -path instead, for the parts in which you are pruning the directories like def/incoming.
find $(INP_PATH} -type f -ls |grep -v "${INP_PATH}/.*/"
By following answer for How to exclude a directory in find . command:
find . \( -name ".git" -o -name "node_modules" \) -prune -o -print
This is what I did to exclude all the .git directories and passed it to -exec for greping something in the
find . -not -path '*/\.*' -type f -exec grep "pattern" [] \;
-not -path '*/\.*' will exclude all the hidden directories
-type f will only list type file and then you can pass that to -exec or whatever you want todo

Resources