4

I am looking to create a bash function to filter all the dotfiles (no directories) in a selected directory. I only need the file name, not the full path.

For the moment, i only have this command:

find . -maxdepth 1 -type f -print0

which prints all the files excluding the dirs. Now i still have to exclude the non-dotfiles. So i've try to pipe the output to grep, like so:

find . -maxdepth 1 -type f -print0 | grep "^\."

and it didn't seem to work. ->

Binary file (standard input) matches

Do you guys have an elegant solution to this?

Thanks!

Jeanmichel Cote
  • 531
  • 1
  • 5
  • 19
  • 1
    try changing your command to find . -maxdepth 1 -type f -print0 | grep -a "^\." – Satya Jul 19 '15 at 02:09
  • did you want only the filenames startswith dot? – Avinash Raj Jul 19 '15 at 02:15
  • Thanks! It works if i run the command directly within the directory. But what i want to do is to be able to assign that command a path in which to run the search... Something like: find $HOME -maxdepth 1 -type f -print0 | grep -a "^\." – Jeanmichel Cote Jul 19 '15 at 02:16

3 Answers3

5

If you want only dot-files:

find . -maxdepth 1 -type f -name '.*' -printf '%f\0'

The test -name '.*' selects dot files. Since -name accepts globs, . means a literal period and * means any number of any character.

The action -printf '%f\0' will print NUL-separated filenames without the path.

If your name selection criteria becomes more complex, find also offers -regex which selects files based on regular expressions. GNU find understands several different dialects of regular expression. These can be selected with -regextype. Supported dialects include emacs (default), posix-awk, posix-basic, posix-egrep, and posix-extended.

Mac OSX or other BSD System

BSD find does not offer -printf. In its place, try this:

find . -maxdepth 1 -type f -name '.*' -exec basename {} \;

Note that this will be safe all file names, even those containing difficult characters such as blanks, tabs or newlines.

Putting the dot files into a bash array

If you want to get all dot files and directories into an array, it is simple:

all=(.*).

That is safe for all file names.

If you want to get only regular files, not directories, then use bash:

a=(); while IFS= read -r -d ''; do a+=("$(basename "$REPLY")"); done < <( find $HOME -maxdepth 1 -type f -name '.*' -print0 )

This is also safe for all file names.

John1024
  • 109,961
  • 14
  • 137
  • 171
1

Get all the hidden files, with the full paths

find . -type f -name '.*'

Get just the filenames of the hidden files:

for f in `find . -type f -name '.*'`; do basename "$f"; done

Because file names can contain white space (blanks, tabs, newlines), however, it is safer to use:

find . -type f -name '.*' -print0 | while IFS= read -r -d '' f; do basename "$f"; done
John1024
  • 109,961
  • 14
  • 137
  • 171
user2182349
  • 9,569
  • 3
  • 29
  • 41
  • Hum, this does not work. It freezes the shell and never return anything. – Jeanmichel Cote Jul 19 '15 at 02:29
  • 2
    The freezing is likely because the test `-maxdepth 1` is missing so `find` searches the whole directory tree. – John1024 Jul 19 '15 at 02:32
  • Works fine now. I got the best results from this answer and adding the -maxdepth 1 option. On top of this, i can use the `$HOME` variable instead of the `.`. But i read somewhere that it is bad practice, securitywise, to do a search with `.*`. – Jeanmichel Cote Jul 19 '15 at 02:41
  • @user2182349 I added to your answer a version of the loop approach that is safe for all file names. (If you don't like it, feel free to revert.) – John1024 Jul 20 '15 at 05:35
  • @John1024 - Thank you! – user2182349 Jul 20 '15 at 11:55
1

You could also use bash globbing/pattern matching:

for f in .[^.]*; do
    [ -f "$f" ] && echo "$f"
done

[^.] is a negating expression to avoid matching . (current working directory) or .. (parent directory).

John B
  • 3,566
  • 1
  • 16
  • 20