2

I'm using bash shell. I want to see a diff of file names in two directories, but only of direct children of those directories. So for instance, if I have these directories with child files/dirs named ...

dir1
-> abc
-> def
-> ghi

dir2
-> abc
-> fff
-> ghi

I would like to be able to run some command that would tell me that dir2 has a file/dir named "fff" that dir1 doesn't have, whereas "dir1" has a child file/dir named "def" that dir2 doesn't have. How do I do that?

2 Answers 2

1
for name in dir1/* dir2/*; do
    bname=${name##*/}

    if [ ! -e "dir1/$bname" ]; then
        printf '"%s" not in dir1\n' "$bname"
    elif [ ! -e "dir2/$bname" ]; then
        printf '"%s" not in dir2\n' "$bname"
    fi
done

This will iterate over all names in dir1 and dir2. $bname will be the basename of the name ($name without path).

If the (base)name can not be found in dir1, then this is reported. Otherwise, if it's not found in dir2 it's reported.

This copes with filenames containing embedded newlines and should run with /bin/sh or any compatible sh shell.

This could potentially be expanded to more than two directories. With bash:

dirs=( dir1 dir2 dir3 dir4 )

for dir in "${dirs[@]}"; do
    for name in "$dir"/*; do

        bname=${name##*/}

        for tdir in "${dirs[@]}"; do
            if [ ! -e "$tdir/$bname" ]; then
                printf '"%s" not found in %s\n' "$bname" "$tdir"
            fi
        done

    done
done

$ tree
.
|-- dir1
|   `-- filename 1
|-- dir2
|   |-- filename 1
|   |-- filename 3
|   `-- hello
world
`-- script.sh

$ sh script.sh
"filename 3" not in dir1
"hello
world" not in dir1
1
  • This is quite nice! Commented Sep 19, 2017 at 19:04
0

For this I quite like comm -3, on plain ls’s output:

$ comm -3 <(ls dir1) <(ls dir2)
def
        fff

Entries in the first column are only in the first directory, entries in the second column are only in the second directory; the -3 option removes the third column which displays entries common to both. You can also specify -1 to only see files present in dir2 but not in dir1, and -2 to only see files present in dir1 but not in dir2.

For this to work you need to ensure you don’t have any options set for ls, either via its OPTIONS configuration or an alias. You can avoid such issues using a variant such as

$ comm -z -3 <(cd dir1; printf "%s\0" *) <(cd dir2; printf "%s\0" *) | tr '\0' '\n'
def
        fff

The latter avoids issues with special characters in filenames. You can combine it with -1 or -2 and drop the tr to feed it into xargs -0 for further processing of either set of files too.

4
  • ls is problematic, f.i. on my Linux machines I have OPTIONS="-b -T 0 --quoting-style=shell". With this a file named a b is listed as 'a b'. Commented Sep 19, 2017 at 19:02
  • Hey this doens't work. It is listing files taht are in both directories and that isn't what I want.
    – Dave
    Commented Sep 19, 2017 at 19:07
  • @Dave see my update, you can change comm’s options to see exactly what you want. Commented Sep 19, 2017 at 20:34
  • @SatōKatsura that can be avoided, see my update. Commented Sep 19, 2017 at 20:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .