0

I want to exclude all C files that have a matching asm file. The assembly files all have _x86_64.S at the end. The C files have identical names, but with .c instead of _x86_64.S at the end. How would I do something like:

find . -not -name *{_x86_64.S but replace with .c}

So, for example, if my_func.c and my_func_x86_64.S exist, then the file my_func.c will be excluded by the above command.

Edit: The answer provided below works. I forgot to mention I was using this in a Makefile. What I had been doing was the following:

ASM_FILES = $(shell find ./src/assembly/x86_64/ -name "*.S" -printf "-not -name \"*%f*\" -and ")
ASM_INCLUDE += -wholename "./src/assembly/x86_64/*.S" -or
EXCLUDE += $(subst _x86_64.S,.c,$(ASM_FILES))

This works, but I was hoping for something a little cleaner.

2
  • Note that -wholename, -printf, -or, -not, -and are all non-standard GNUisms (well, the last 3 actually from BSDs I believe), so shouldn't be used in a Makefile unless that's to build software intended for GNU systems only. Commented Nov 7, 2024 at 6:23
  • Yes, I am unfortunately aware. I spent a very long time trying to create a POSIX compliant Makefile. The problem is conditionals do not seem possible in the standard. GNU Make has one method, BSD make has another. On FreeBSD you can run pkg install gmake so I abandoned aiming at POSIX compliance after much effort. If I am mistaken, and conditionals are possible (i.e. ifeq), please let me know! Commented Nov 8, 2024 at 13:08

1 Answer 1

0

Well, something like [ -e "${1%.c}.asm" ] would test if the .c file named by the first command line arg has a corresponding .asm.

So, remembering that find's -exec is a condition, this can be used to print all .c files without a corresponding .asm.

find . -name "*.c" -exec sh -c '! [ -e "${1%.c}.asm" ]' find-sh {} \; -print

Then, we'll have to include everything else (assuming you only want regular files), e.g.:

find . -type f \( ! -name "*.c" -o
                  -name "*.c" -exec sh -c '! [ -e "${1%.c}.asm" ]' find-sh {} \; \
               \) -print

e.g. given the four files bar.c foo.asm foo.c hello.txt, that prints

./hello.txt
./foo.asm
./bar.c

Though that may be slightly slow, since it forks a new shell instance for each .c file.

Alternatively, you could do that in a shell loop with ** for recursive globbing (needs shopt -s globstar in Bash):

$ shopt -s globstar
$ for f in ./**/* ; do
    case $f in *.c)
        if [ -e "${f%.c}.asm" ]; then continue; fi
    esac;
    printf -- "%s\n" "$f"
  done
./bar.c
./foo.asm
./hello.txt

(Note that there are differences between shells on how ** deals with symlinks within the tree.)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.