Order of actions in find command - linux

This is the action I want to take place:
find ~ -type f -not -perm 0600 -ls
But when I try to write it this way
find ~ -ls \( -type f -not -perm 0600 \)
In which I tried to override the evaluation sequence via () operators the -ls test option always performed before the evaluation of the expression in the parenthesis,the question is doesn't parenthesis have effect on test options of find command or what?

Related

what's meaning of `find \( -perm -04000 -o -perm -02000 \)`

I know this:
find . -perm -664
Search for files which have read and write permission for their owner and group,
and which other users can read.
but I can't figure out what is meaning of 04000 and 02000, maybe the lsattr could tell me? but I also have no idea about that.
THX.
I've come to the conclusion that both commands below give the same output:
SUID permission search command:
find / -perm -u=s -type f -ls 2>/dev/null
find / -perm -04000 -type f -ls 2>/dev/null
and also:
SGID permission search command:
find / -perm -g=s -type f -ls 2>/dev/null
find / -perm -02000 -type f -ls 2>/dev/null
Therefore find \( -perm -04000 -o -perm -02000 \) is searching for the permission bits of SUID and SGID. Documentation here: https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html

Combine two find commands in one

I currently have the following line:
find . -type f ! -name "*.xml" -delete && find . -type d -empty -delete
But as they are 2 find commands, I suppose I can merge them into one. But I don't want to use -exec.
I wan't to use something native in bash, that comes preinstalled with any linux distro.
You can group primaries with the -o ("or", as opposed to the implict "and" that find applies to its primaries) primary. The parentheses are escaped to avoid shell syntax errors and ensure they are passed as arguments to find.
find . \( \( -type f ! -name "*.xml" \) -o \( -type d -empty \) \) -delete

Using "find … -delete" to remove image files only from special subdirectories

I need to clean up some subdirectories inside my music collection from unwanted images. I’m using Ubuntu 14.04 and/or Linux Mint 17.2 and a bash shell script using the find command.
The directory structure (minimal example) is as follows:
Tagged
Artist #1
Artist #1 - An Album
Artist #1 - A Track.flac
cover.jpg
something.png
Artist #1 - [compilations]
Artist #1 - A Track.flac
cover.jpg
something.png
Artist #2
Artist #2 - Another Album
Artist #2 - A Track.mp3
cover.jpg
Only in the subfolders ending with "[compilations]", I want to delete all kind of jpeg and/or png images (because the tagging software erroneously puts them there). Any images that happen to be in normal "album" folders I wish too keep.
With my directory structure, folders with "[compilations]" in the name can only happen just below the "Artist" folders, repectively; so a maximum of two levels deep.
I came up with the following find command:
$ cd Tagged
$ find . -maxdepth 2 -type d -name '*\[compilations\]' -exec find {} -type f -iname '*.jp*g' -or -iname '*.png' -delete \;
This seems to do something and takes a while, but the files "./Artist #1/Artist #1 - [compilations]/cover.jpg" and "./Artist #1/Artist #1 - [compilations]/something.png" are still there (and all other image files).
Being quite new to Linux, I assume I make a dumb mistake using find's -delete option, because the following command (without -delete) shows the files correctly:
$ find . -maxdepth 2 -type d -name '*\[compilations\]' -exec find {} -type f -iname '*.jp*g' -or -iname '*.png' \;
./Artist #1/Artist #1 - [compilations]/cover.jpg
./Artist #1/Artist #1 - [compilations]/something.png
So here are my questions:
Why does the -delete not work?
Is this command really safe regarding "extravaganza" like whitespace, glob characters and foreign characters in the paths and filenames?
How would I have to rewrite the above command, still using bash and find?
Could the command be optimized (re speed, safety, nested finds)?
In the actual collection, the command must traverse 16899 folders, almost all of them contain whitespace and foreign characters (like Czech, Russian, Japanese, Greek, German …), so it must be robust.
Thanks in advance for any insights and some enlightenment!
Your -delete predicate only applies to the
-iname '*.png'
predicate, because you missed groupings: when you give find the following:
-type f -iname '*.jp*g' -or -iname '*.png' -delete
because of the precedence of the boolean operators, find understands:
\( -type f -iname '*.jp*g' \) -or \( -iname '*.png' -delete \)
To fix this, use:
-type f \( -iname '*.jp*g' -or -iname '*.png' \) -delete
I'd suggest that to experiment you replace -delete with -print: you'll see what the -delete applies to!
Now, regarding your nested find: because of the structure of your directory tree (your files are only in depth 3), you should be able to do with only one instance of find:
find -maxdepth 3 -path '*/*\[compilations\]/*' \( -iname '*.jp*g' -o -iname '*.png' \) -type f -print
(I put -print instead of -delete so that you can check the command before executing it with -delete).
After some experimentation, I think my error was in not putting the OR'ed part in parentheses—it seems find used the -delete only on the right part of the last OR, i.e., tried to delete '*.png'. Alas, almost all of my cover images were '*.jpg' so I thought it wouldn't work at all!
So I think the corrected command should be:
$ find . -depth -maxdepth 2 -type d -name '*\[compilations\]' -exec find {} -type f \( -iname '*.jp*g' -or -iname '*.png' \) -delete \;
It seems to work correctly on my test case above.
Nevertheless, some confirmation would be nice. An maybe some answers to my other questions, just for information and learning. Thank you!

Recursively set user permissions equal to group permissions without changing anything else

Currently works for me (inspired from here):
sudo find . \( \( -type d -o -type f \) ! -path ".git/*" \) -printf '%04m%p\0' | perl -n0e 'unless (/^.(.)\1/) {$p1=substr($_,1,1);$p2=substr($_,2,1);$p3=substr($_,3,1);$n=substr($_,4);system("sudo chmod $p1$p1$p3 $n");}'
But seems to be complex and very inefficient.
Thus, is it possible to achieve that in a more simple and/or efficient way?
Thanks!

find -name "*.xyz" -o -name "*.abc" -exec to Execute on all found files, not just the last suffix specified

I'm trying to run
find ./ -name "*.xyz" -o -name "*.abc" -exec cp {} /path/i/want/to/copy/to
In reality it's a larger list of name extensions but I don't know that matters for this example. Basically I'd like to copy all those found to another /path/i/want/to/copy/to. However it seems to only be executing the last -name test in the list.
If I remove the -exec portion all the variations of files I expect to be found are printed out.
How do I get it to pass the full complement of files found to -exec?
find works by evaluating the expressions you give it until it can determine the truth value (true or false) of the entire expression. In your case, you're essentially doing the following, since by default it ANDs the expressions together.
-name "*.xyz" OR ( -name "*.abc" AND -exec ... )
Quoth the man page:
GNU find searches
the directory tree rooted at each given file name by evaluating the
given expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known (the left
hand side is false for and operations, true for or), at which point
find moves on to the next file name.
That means that if the name matches *.xyz, it won't even try to check the latter -name test or -exec, since it's already true.
What you want to do is enforce precedence, which you can do with parentheses. Annoyingly, you also need to use backslashes to escape them on the shell:
find ./ \( -name "*.xyz" -o -name "*.abc" \) -exec cp {} /path/i/want/to/copy/to \;
More usable than Jaypal's solution would maybe be:
find ./ -regex ".*\.\(jpg\|png\)" -exec cp {} /path/to
find . \( -name "*.xyz" -o -name "*.abc" \) -exec cp {} /path/i/want/to/copy/to \;
It may work:
find ./ -name "*.{xyz,abc}" -exec cp {} /path/i/want/to/copy/to

Resources