Search code examples
bashgrep

Compare outputs of two `find` commands


I have a folder with sub-folders as below (for the sake of the question, I have simplified)

I am able to find the folders where check.lock files are present using find . -type f -name "check.lock"

and find the folders where local.lock files are present using find . -type f -name "local.lock"

As you can see variant2 is folders binary_test and component_test do NOT have the local.lock files (but they have the check.lock files).

How can I find the these (do not have to the variant names, the whole paths are sufficient). In other words, the output of find . -type f -name "check.lock" will be 9 lines & the output of find . -type f -name "local.lock" will be 6 lines & I want those 3 lines where the "local.lock" was not found.

so I want to take the output of find . -type f -name "check.lock" and then compare that with find . -type f -name "local.lock" and do a grep (with unit_test/variant1 or something like that) so that I get a smaller list to visually compare.

I tried { find . -type f -name "check.lock" && find . -type f -name "local.log"; } | grep "unit_test" but the output is strange (shows only one line with "check.lock"`

Any helpy is appreciated. If you can at least let me know how to pass out of 2 commands into grep, that would also help.

├── src
│   ├── unit_test
│   │   ├── variant1
│   │   │   ├── check.lock
│   │   ├── variant2
│   │   │   ├── check.lock
│   │   ├── variant3
│   │   │   ├── check.lock
│   ├── binary_test
│   │   ├── variant1
│   │   │   ├── check.lock
│   │   ├── variant2
│   │   │   ├── check.lock
│   │   ├── variant3
│   │   │   ├── check.lock
│   ├── component_test
│   │   ├── variant1
│   │   │   ├── check.lock
│   │   ├── variant2
│   │   │   ├── check.lock
│   │   ├── variant3
│   │   │   ├── check.lock
├── public
│   ├── unit_test
│   │   ├── variant1
│   │   │   ├── local.lock
│   │   ├── variant2
│   │   │   ├── local.lock
│   │   ├── variant3
│   │   │   ├── local.lock
│   ├── binary_test
│   │   ├── variant1
│   │   │   ├── local.lock
│   │   ├── variant2
│   │   ├── variant3
│   │   │   ├── local.lock
│   ├── component_test
│   │   ├── variant1
│   │   │   ├── local.lock
│   │   ├── variant2
│   │   ├── variant3
│   │   │   ├── local.lock


Solution

  • My "onleliner" for comparing two differents tree

    1. First approach, using diff -y:

    diff -y <( cd src && find * -type f -name check.lock -printf %h\\n | sort
       ) <( cd public && find * -type f -name local.lock -printf %h\\n | sort )
    
    binary_test/variant1                        binary_test/variant1
    binary_test/variant2                      <
    binary_test/variant3                        binary_test/variant3
    component_test/variant1                     component_test/variant1
    component_test/variant2                   <
    component_test/variant3                     component_test/variant3
    unit_test/variant1                          unit_test/variant1
    unit_test/variant2                          unit_test/variant2
    unit_test/variant3                          unit_test/variant3
    

    This work in both directions:

    mkdir public/component_test/variantZ
    touch public/component_test/variantZ/local.lock
    diff -y <( cd src && find * -type f -name check.lock -printf %h\\n | sort
      ) <(  cd public && find * -type f -name local.lock -printf %h\\n | sort )
    
    binary_test/variant1                        binary_test/variant1
    binary_test/variant2                      <
    binary_test/variant3                        binary_test/variant3
    component_test/variant1                     component_test/variant1
    component_test/variant2                   <
    component_test/variant3                     component_test/variant3
                                              > component_test/variantZ
    unit_test/variant1                          unit_test/variant1
    unit_test/variant2                          unit_test/variant2
    unit_test/variant3                          unit_test/variant3
    

    1.1 With files names binded by sed

    This whill show both directories side by side, with symbol < or > when some file seem missing:

    diff -y <(
        cd src &&
            find * -type f -name "check.lock" |
                sort |
                sed 's/check.lock/local.lock/'
      ) <(
        cd public &&
            find * -type f -name "local.lock" |
            sort
    ) |
        sed 's/^\([^[:space:]]*\)local.lock/\1check.lock/'
    
    binary_test/variant1/check.lock             binary_test/variant1/local.lock
    binary_test/variant2/check.lock           <
    binary_test/variant3/check.lock             binary_test/variant3/local.lock
    component_test/variant1/check.lock          component_test/variant1/local.lock
    component_test/variant2/check.lock        <
    component_test/variant3/check.lock          component_test/variant3/local.lock
                                              > component_test/variantZ/local.lock
    unit_test/variant1/check.lock               unit_test/variant1/local.lock
    unit_test/variant2/check.lock               unit_test/variant2/local.lock
    unit_test/variant3/check.lock               unit_test/variant3/local.lock
    

    Where 1st column show src and 2nd show public.

    Final sed command will replace local.lock by check.lock only if present before any space.

    Variant using some cosmetic:

    diff <(
        cd src &&
            find * -type f -name "check.lock" |
                sort |
                sed 's/check.lock/local.lock/'
    ) <(
        cd public &&
            find * -type f -name "local.lock" |
            sort
    ) |
    sed '/^[<>]/!d;s/^/Missing in /;s/</public:/;
         />/{s/>/src:/;s/local.lock/check.lock/;};'
    
    Missing in public: binary_test/variant2/local.lock
    Missing in public: component_test/variant2/local.lock
    Missing in src: component_test/variantZ/check.lock
    

    2.Some other approach

    2.1 unidirectional, find | sed | sh

    By using syntax sed < <(...), I will confine cd in a subshell, which won't affect current environment.

    sed < <(
        cd src &&
            find * -type f -name "check.lock"
      ) -e 's/\(.*\)check.lock/test -f \1local.lock || \
                                   echo public\/\1local.lock missing/;
             1icd public
      '  | sh
    
    public/binary_test/variant2/local.lock missing
    public/component_test/variant2/local.lock missing
    

    2.2 unidirectional, too, but pure bash:

    Using rare option -c and -C with mapfile command.

    existInPublic() {
        [[ -e public/${2/check/local} ]] || echo "public/${2/check/local} missing."
    }
    shopt -s globstar
    mapfile -tc1 -CexistInPublic _ < <(cd src && printf %s\\n **/check.lock)
    
    public/binary_test/variant2/local.lock missing.
    public/component_test/variant2/local.lock missing.
    

    2.3 bidirectional, pure bash and without loop:

    In fact there is a loop of only 2 cycles: for grp in src public, but no loop over list of files. This could render quickly even on big trees.

    diffSrcPublic() {
        local grp varnam fstr grp2
        local -i idx=1 uShoptGlobstar=0
        local -Ai diffList='()'
        if ! shopt -q globstar; then uShoptGlobstar=1;shopt -s globstar; fi
        for grp in src public; do
            varnam=($grp/**/*.lock)
            varnam=(${varnam[@]#$grp/})
            varnam=(${varnam[@]%/*.lock})
            printf -v fstr 'diffList["%%s"]+=%d\\n' $((idx))
            idx=$(( idx << 1 ))
            . <(printf "$fstr" "${varnam[@]}")
        done
        printf -v fstr '%s:%%s  ' "${diffList[@]}"
        printf -v fstr "$fstr" "${!diffList[@]}"
        read -a grp <<< "$fstr"
        grp=(${grp[@]/%3:*})
        grp2=(${grp[@]/%2:*})
        grp=(${grp[@]/%1:*})
        printf 'Missing in src: %s\n' ${grp[@]#2:}
        printf 'Missing in public: %s\n' ${grp2[@]#1:}
        (( uShoptGlobstar )) && shopt -u globstar
    }
    diffSrcPublic
    
    Missing in src: component_test/variantZ
    Missing in public: component_test/variant2
    Missing in public: binary_test/variant2