For convenience I've created links to a rather large "static" folder inside all of my www\site1, www\site2, etc. folders.
From www\ I would like to find all files starting with test_ in all subdirectories, without recursing into static for all sites.
I have gnuwin32 installed, which includes GNU find version 4.2.20, but its symlink options don't seem aware of Windows junctions ("symlinks" created with mklink /j source target). The closest I've gotten is:
find . -path "*static*" -prune -o -type f -name "test_*"
which sort of works. It's a little unsatisfying since it's not very general, and it also returns all the static folders (but not their contents).
I thought
dir /s /b /a:-L test_*
would work, but that seems to only omit the actual junctions and not their subdirectories.
Is there a way to do this?
Why the pruned directory still gets printed
If you don't include an explicit action to your find it will imply a -print gets applied to the entire expression, so
find . -path "*static*" -prune -o -type f -name "test_*"
actually gets executed as
find . `\( \( -path "*static*" -a -prune \) -o \( -type f -name "test_*" \) \) -print
So if either the left side or the right side of the exp OR exp1 is true, find still prints the file. If you want left side to be omitted, you can just add an explicit action to the right side
find . -path "*static*" -prune -o -type f -name "test_*" -print
Following symlinks
find does not follow symlinks by default. Try adding -L, though I'm not sure if this will work with windows links
-L Follow symbolic links. When find examines or prints information about files, the information used
shall be taken from the properties of the file to which the link points, not from the link itself
(unless it is a broken symbolic link or find is unable to examine the file to which the link
points). Use of this option implies -noleaf. If you later use the -P option, -noleaf will still
be in effect. If -L is in effect and find discovers a symbolic link to a subdirectory during its
search, the subdirectory pointed to by the symbolic link will be searched.
When the -L option is in effect, the -type predicate will always match against the type of the
file that a symbolic link points to rather than the link itself (unless the symbolic link is
broken). Using -L causes the -lname and -ilname predicates always to return false.
Related
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.
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).
I have never been able to fully understand the -prune action of the find command. But in actuality at least some of my misunderstanding stems from the effect of omitting the '-print' expression.
From the 'find' man page..
"If the expression contains no actions other than -prune, -print is performed on all files for which the expression is true."
.. which I have always (for many years) taken to mean I can leave out '-print'.
However, as the following example illustrates, there is a difference between using '-print' and omitting '-print', at least when a '-prune' expression appears.
First of all, I have the following 8 directories under my working directory..
aqua/
aqua/blue/
blue/
blue/orange/
blue/red/
cyan/blue/
green/
green/yellow/
There are a total of 10 files in those 8 directories..
aqua/blue/config.txt
aqua/config.txt
blue/config.txt
blue/orange/config.txt
blue/red/config.txt
cyan/blue/config.txt
green/config.txt
green/test.log
green/yellow/config.txt
green/yellow/test.log
My goal is to use 'find' to display all regular files not having 'blue' as part of the file's path. There are five files matching this requirement.
This works as expected..
% find . -path '*blue*' -prune -o -type f -print
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./aqua/config.txt
But when I leave out '-print' it returns not only the five desired files, but also any directory whose path name contains 'blue'..
% find . -path '*blue*' -prune -o -type f
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./cyan/blue
./blue
./aqua/blue
./aqua/config.txt
So why are the three 'blue' directories displayed?
This can be significant because often I'm trying to prune out a directory structure that contains more than 50,000 files. When that path is processed my find command, especially if I'm doing an '-exec grep' to each file, can take a huge amount of time processing files for which I have absolutely no interest. I need to have confidence that find is not going into the pruned structure.
The implicit -print applies to the entire expression, not just the last part of it.
% find . \( -path '*blue*' -prune -o -type f \) -print
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./cyan/blue
./blue
./aqua/blue
./aqua/config.txt
It's not decending into the pruned directories, but it is printing out the top level.
A slight modification:
$ find . ! \( -path '*blue*' -prune \) -type f
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./aqua/config.txt
(with implicit -a) would lead to having the same behavior with and without -print.
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.
I'm building a backup script where some directories should not be included in the backup archive.
cd /;
find . -maxdepth 2 \
\( -path './sys' -o -path './dev' -o -path './proc' -o -path './media' -o -path './mnt' \) -prune \
-o -print
This finds only the files and directories I want.
Problem is that cpio should be fed with the following option in order to avoid problems with permissions when restoring files.
find ... -depth ....
And if I add the -depth option, returned files and directories include those I want to avoid.
I really don't understand these sentences from the find manual:
-prune True; if the file is a directory, do not descend into it. If
-depth is given, false; no effect. Because -delete implies
-depth, you cannot usefully use -prune and -delete together.
I am quoting a passage from this tutorial which might offer better understanding of -prune option of find.
It is important to understand how to prevent find from going too far.
The important option in this case is -prune. This option confuses people because it is always true. It has a side-effect that is important. If the file being looked at is a directory, it will not travel down the directory. Here is an example that lists all files in a directory but does not look at any files in subdirectories under the top level:
find * -type f -print -o -type d -prune
This will print all plain files and prune the search at all directories. To print files except for those in a Source Code Control Directories, use:
find . -print -o -name SCCS -prune
If the -o option is excluded, the SCCS directory will be printed along with the other files.
Source