3

I've checked some answers online, but they do not work when there's a -name parameter in the command (maybe I'm doing something wrong, not sure). What I'd like to do is to list only filenames (not the directory/path), and to complicate more, I have same filename with different extension, so I need to filter the result using -name and -o, the trouble is the suggestions that I saw use either -printf or basename, and those doesn't work well when there's -name *.txt -o name *.pdf. Is there any other way to solve this problem?

Example of my files:

/dir1/dir2/dir3/dir4/
                    /file1.txt
                    /file1.pdf
                    /file1.ods ...etc.

I'm only interested in listing one or more type of file per listing (by using -name to filter my results.

Is it possible to accomplish it using only 1 find command or I have to do it in 2 steps (saving the result in a temporary file, then filter/strip the directory from the temp file)? The result I'm trying to accomplish is a text file with filename per line:

file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.

The command I was using is

find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"

I am using GNU find.

Update

It turned out I have to put \(...\), as reminded by αғsнιη

AdminBee
  • 22,803
michaelbr
  • 111
  • Relating https://unix.stackexchange.com/a/70618/117549 and https://unix.stackexchange.com/a/46259/117549 in the case of GNU find... – Jeff Schaller Jan 06 '21 at 13:39
  • @Jeff Schaller thanks for your feedback, yes, I'm using GNU find, I've tried that, but when I used -printf and -name *.txt -o -name *.pdf it'll list only the last one, not both, maybe I'm doing something wrong. I'm not that familiar with Linux. – michaelbr Jan 06 '21 at 13:53

2 Answers2

5

When using multiple expressions in logical OR mode with -o you need enclose them all within \( ... \), else the last expression result is always respected; and -printf '%f\n' is used to print the files' name only with any leading directories removed.

find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'

see man find under OPERATORS section about ( expr ):

OPERATORS
...

( expr )
Force precedence. Since parentheses are special to the shell, you will normally need to quote them.

so you can either use \( ... \) or '(' ... ')' (whitespaces after and before parenthesis are important) to escape them.

αғsнιη
  • 41,407
1

As has already been mentioned, you need to group your -name tests in parentheses. These parentheses need to be escaped as \( ... \) or quoted as e.g. '(' ... ')' to protect them from the shell (where they would otherwise designate a subshell).

Using standard find, and also making handling the filename suffixes slightly easier:

set -- txt pdf ods

for suffix do set -- "$@" -o -name "*.$suffix" shift done shift

find /dir1/dir2/dir3/dir4 ( "$@" ) -type f -exec basename {} ;

This first sets the positional parameters to the list of filename suffixes that we'd like to see (this could also be taken from the command line if this is a script), and constructs the OR-list of -name tests. This is then used in the find command line.

The the find command itself then calls the basename utility for each found name.

As an alternative, we could instead use a parameter substitution to remove all but the filename component of each pathname, but to do so we must pass the pathnames to an inline sh -c script:

# [...] as before [...]

find /dir1/dir2/dir3/dir4 ( "$@" ) -type f -exec sh -c ' for pathname do printf "%s\n" "${pathname##*/}" done' sh {} +

I would expect this to run quicker than the variation using basename, at least if there are many filenames to handle, although with GNU basename from coreutils, you could use -a or --multiple likes so:

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +

This would call basename as few times as possible. But then again, if you had coreutils installed, you may well have findutils too, with GNU find, and could use -printf '%f\n' to do the same thing.

Kusalananda
  • 333,661
  • Thanks αғsнιη and Kusalananda for your feedback and detailed explanation, it's much appreciated. With Linux, you never stops learning. – michaelbr Jan 07 '21 at 19:43