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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.