1

I'd like to use a bash script or command to find files that have very similar names but differ only in a part inside the brackets. e.g.

Filename (year1)
Filename (year2)

should match.

Or more specifically,

Filename (2000)
Filename (2001)

should match.

0

2 Answers 2

1

The following script works by setting the list of positional parameters to the list of names matching the pattern *' ('*')', i.e., all files with names that contain a space followed by parenthesis at the end, in the current directory.

The script uses a double loop to compare each name against all others. To avoid comparing some name A against some other name B and then B against A, we shift off the first element of the list at the start of the outer iteration. This also avoids testing a name A against itself.

Inside the inner loop, we remove the parenthesis at the end of the names and compare the two resulting strings. If the strings are the same, we print the names.

#!/bin/sh

set -- *' ('*')'

for name do
        shift

        for dup do
                if [ "${name%% (*)}" = "${dup%% (*)}" ]; then
                        printf '"%s" <-> "%s"\n' "$name" "$dup"
                fi
        done
done
5
  • @Cbhihe The edits by αғsнιη was purely formatting. The meaning of the question was never changed. I don't see where it say that there should only be a single character difference. If the original meaning of the OP was modified, I can't see it. gawk and python would need to take special care with names containing newlines, which my code deals with.
    – Kusalananda
    Commented Oct 5, 2021 at 18:03
  • @Cbhihe Yes, the comments mentioned two cases of duplication, between something1 (year1) and something1 (year2) and between something2 (year3) and something2 (year4), if I recall correctly. So something1 is duplicated and something2 is duplicated, but I never thought they would be duplicates of each other, and that's not what the question says or have ever said.
    – Kusalananda
    Commented Oct 5, 2021 at 18:15
  • So the logic of the script makes sense to me, but when I paste it into a script and run it, it doesn't find duplicates. e.g. "<filenameprefix> (2019).ext" isn't matched to "<filenameprefix> (2021).ext" I get no output. I've tried on MacOS and Ubuntu. Commented Oct 5, 2021 at 19:11
  • I think because I neglected to say there was a file extension :facepalm: Commented Oct 5, 2021 at 19:26
  • @ShowtimeMonkey So, change set -- *' ('*')' to set -- *' ('*').ext' and then also change ${name%% (*)} to ${name%% (*).ext}, and similarly for dup in that test.
    – Kusalananda
    Commented Oct 5, 2021 at 20:07
0

Though late, I thought I'd contribute a small Bash script that processes filenames with Gawk. It flags a variety of dupes among files in any given directory. The processing remains O(n^2), although file pairs are checked only once.

Run the script in the directory of interest, for instance in a directory that contains:

$ \ls -A -1
2323- (1236).suffix
23232(1234).suffix
23232 (1236).hsj
23232 (1236).suffix
hello(2001.10.29)fgh.ssh
hello(2002.10.29)fgh.ssh
23232(1236).suffix
23232(12 6).suffix
23232 (1286).suffix
23232 (1446).suffix
23232(3236).suffix
dwlkl(1234).sds

Now the script:

$ cat near_match.sh
#!/usr/bin/env bash
gawk '
    {i++; a[i] = $0; next}  # put files to check in array 'a'
    END {
        nfiles = i; # number of files to check
        for (i = 1; i <= nfiles-1; i++) {
            lblkarr1 = split(a[i], blkarr1, "[()]");
            lparr1 = split(blkarr1[2], parr1, "");
            for (j = i+1; j <= nfiles; j++) {
                lblkarr2 = split(a[j], blkarr2, "[()]");
                lparr2 = split(blkarr2[2], parr2, "");
                mismatch = 0;
                if ("x"blkarr1[1]"x" == "x"blkarr2[1]"x" && "x"blkarr1[3]"x" == "x"blkarr2[3]"x" && lparr1 == lparr2) {
                    for (k=1; k<=length(blkarr1[2]); k++) { if (parr1[k] != parr2[k]) mismatch++ };
                    if (mismatch == 1)  printf "dupes: %s  <-->  %s\n", a[i], a[j];
                }
            }
        }
    }' <<< $(\ls -A -1 -- *"("*")"*)

exit 0

Make the script executable and run with no arguments.

$ near_match.sh
dupes: 23232(1234).suffix  <-->  23232(1236).suffix
dupes: 23232 (1236).suffix  <-->  23232 (1286).suffix
dupes: hello(2001.10.29)fgh.ssh  <-->  hello(2002.10.29)fgh.ssh
dupes: 23232(1236).suffix  <-->  23232(12 6).suffix
dupes: 23232(1236).suffix  <-->  23232(3236).suffix
  • As specified by OP, "dupe" are exclusively defined as 1-character mismatches, inside the parentheses. Adapting the solution to extend it so character mismatches can be detected anywhere in filename is trivial.
  • The parentheses can be anywhere in the filename and the filename can have any suffix o none.
  • The proposed solution seems to be robust against special characters inside the filename, except additional parentheses.
  • Changing the 1-character mismatch to a multiple-character mismatch is possible with minimal changes. It would be trivial to vary that number by means of an argument fed to the script. Ask if you must.

HTH

You must log in to answer this question.

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