Home > Software design >  Bash find: exec in reverse oder
Bash find: exec in reverse oder

Time:07-26

I am iterating over files like so:

find $directory -type f -exec codesign {} \; 

Now the problem here is that files on a higher hierarchy are signed first. Is there a way to iterate over a directory tree and handle the deepest files first?

So that

/My/path/to/app/bin

is handled before

/My/path/mainbin

CodePudding user response:

Yes, just use -depth:

-depth

The primary shall always evaluate as true; it shall cause descent of the directory hierarchy to be done so that all entries in a directory are acted on before the directory itself. If a -depth primary is not specified, all entries in a directory shall be acted on after the directory itself. If any -depth primary is specified, it shall apply to the entire expression even if the -depth primary would not normally be evaluated.

For example:

$ mkdir -p top/a/b/c/d/e/f/g/h
$ find top -print
top
top/a
top/a/b
top/a/b/c
top/a/b/c/d
top/a/b/c/d/e
top/a/b/c/d/e/f
top/a/b/c/d/e/f/g
top/a/b/c/d/e/f/g/h
$ find top -depth -print
top/a/b/c/d/e/f/g/h
top/a/b/c/d/e/f/g
top/a/b/c/d/e/f
top/a/b/c/d/e
top/a/b/c/d
top/a/b/c
top/a/b
top/a
top

Note that at a particular level, ordering is still arbitrary.

CodePudding user response:

Using GNU utilities, and decorate-sort-undecorate pattern (aka Schwartzian transform):

find . -type f -printf '%d %p\0' |
    sort -znr                    |
    sed -z 's/[0-9]* //'         |
    xargs -0 -I@ echo codesign @

Drop the echo if the output looks ok.

CodePudding user response:

How about sorting the output of find in descending order:

while IFS= read -d "" -r f; do
    codesign "$f"
done < <(find "$directory" -type f -print0 | sort -zr)
  • <(command ..) is a process substitution which feeds the output of the command to the read command in while loop via the redirect.
  • -print0, sort -z and read -d "" combo uses a null character as a file delimiter. It is useful to protect filenames which include special characters such as whitespace.

CodePudding user response:

I don't know if there is a native way in find, but you may pipe the output of it into a loop and process it line by line as you wish this way:

find . | while read file; do echo filename: "$file"; done

In your case, if you are happy just reversing the output of find, you may go with something like:

find $directory -type f | tac | while read file; do codesign "$file"; done

CodePudding user response:

If I understand correctly, you want /My/path/mainbin to be processed after /My/path/to/app/bin.
find doesn't have an option for that but you can specify its arguments in the order that you need, so, in combination with bash you could do:

#!/bin/bash
shopt -s extglob

find /My/path/!(mainbin) /My/path/mainbin -type f -exec codesign {} \;

That will ensure that mainbin is the last one to be processed

CodePudding user response:

Using find's -depth option as my other answer, or naive sort as some others, only ensures that sub-directories of a directory are processed before the directory itself, but not that the deepest level is processed first.

For example:

$ mkdir -p top/a/b top/a/c/d
$ find top -depth -print
top/a/c
top/a/b/d
top/a/b
top/a
top

For overall deepest level to be processed first, the ordering should be something like:

top/a/b/d
top/a/c
top/a/b
top/a
top

To determine this ordering, the entire list must be known, and then the number of levels (ie. /) of each path counted to enable ranking.

A simple-ish Perl script (assigned to a shell function for this example) to do this ordering is:

$ dsort(){
    perl -ne '
        BEGIN { $/ = "\0" } # null-delimited i/o

        $fname[$.] = $_;
        $depth[$.] = tr|/||;

        END {
            print
                map { $fname[$_] }
                sort { $depth[$b] <=> $depth[$a] }
                keys @fname 
        }
    '
}

Then:

$ find top -print0 | dsort | xargs -0 -I@ echo @
top/a/b/d
top/a/c
top/a/b
top/a
top
  • Related