1

I like to have every file I might want one keybinding away. Currently I have a shell script with these three find commands that I pipe into fzf. But for a variety of reasons I need to combine them into one command (not the least because my current approach is ugly and I am sure flawed).

find ~  \! \( -path */.git/* \) -type d

find ~  \(  -iname \*.Rmd -o -iname \*.el \)  -a \! \( -iname index.txt  -o -path */.thunderbird/* -o -path */python3.8/*  -o -path */.git/* \) -type f

find ~ -regextype posix-extended \( -not  -regex ".*/\.sw(o|p)" -a -not -regex ".*\~$"  \)   -maxdepth 1 -type f

If anyone would like to critisize my ugly find commands and suggest a more efficient/clean way of whitelisting/blacklisting file extensions -- I would be grateful.

EDIT I have simplified the commands to make it easier to understand their function.

  • The first command looks for directories under the home path ~ but excludes .git directories and their subdirectories.
  • The second command finds files in the home directory specifying whitelisted file extensions and then blacklisted file extensions and paths.
  • The third finds dotfiles in my home directory ~ but excludes certain regexs.
2
  • what is the intended function of each of the commands?
    – jsotola
    Aug 19, 2021 at 4:13
  • Thanks for the question. I have updated the post to answer this question and avoid unnessary complication.
    – jds
    Aug 20, 2021 at 13:03

1 Answer 1

2

Maybe:

LC_ALL=C find ~/.                 \
  '('                             \
     -name .git -o                \
     -name .thunderbird -o        \
     -name python3.8              \
  ')' -prune -o                   \
  '('                             \
     -type d -o                   \
     '('                          \
        -name '*.[rR][mM][dD]' -o \
        -name '*.[eE][lL]' -o     \
          ! -path '*/./*/*'       \
          ! -name '.sw[op]'       \
          ! -name '*~'            \
     ')'                          \
     -type f                      \
  ')' -print

(here using standard find syntax (avoiding the -regex, -regex-type, -iname, -maxdepth, -not, -maxdepth which are all GNU extensions)).

Note that the -prune will globally skip all the .git/.thunderbird/python3.8 directories, find will not even both looking into them. That will make a difference compared to your first find command which still prints the .thunderbird and python3.8 directories, and the ones found within.

To be able to report the files at depth 1 (as with the -maxdepth 1 of your third command), we append /. to the search dir, and exclude the files whose path matches */./*/* (more than 2 levels below /./). That means however that paths will be printed as /home/you/./file.txt. If you don't like that /./, you can always get rid of it by piping the output to | sed 's|/\./||;1s|/\.$//'.

Note that your ! -iname index.txt is redundant, as index.txt won't match -iname \*.Rmd -o -iname \*.el anyway. You also forgot the quotes around */.git/* which could get expanded by the shell.

You may also want to replace the -print with -print0 and use fzf --read0 so it also works on file paths that contain newline characters.

4
  • What is the "LC_ALL=C" doing in this command?
    – jds
    Aug 22, 2021 at 12:32
  • @jds, with GNU find (and probably a few others), since -name '*.txt' matches on any number of characters (*) followed by .txt, if we want it to match on anything that ends in .txt, we need to be in a locale where any sequence of bytes form valid characters. The C / POSIX locale is one of those (it's also the only one you're guaranteed to find on any system). You'll find that find . -name '*' doesn't match on the file created with touch $'St\xe9phane.txt' if you're in a UTF-8 locale (where that $'St\xe9phane' is Stéphane encoded in iso8859-1 instead of UTF-8) Aug 22, 2021 at 13:24
  • (continued) so I've gotten used to using LC_ALL=C whenever I use find with a name or path pattern, especially when missing files could have some bad consequences. In your case it would be problematic for ! -path '*/./*/*' as that would mean that /home/you/./dir/other/dir/di<0x80>rectory/some-file.with-wrong-extension would not be excluded. You may not want to use LC_ALL=C when the patterns themselves contain non-ASCII characters (especially with the non-standard -iname/-ipath...) or when you want an exact count of characters in the locale's encoding. Aug 22, 2021 at 13:39
  • See also Limit POSIX find to specific depth? Aug 22, 2021 at 13:52

You must log in to answer this question.

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