8

I am aware of using .[!.]* to refer to all dotfiles in a directory with the exception of .., but how might one refer to all dotfiles except for .. and .git? I have tried several variations on .[!.||.git]* and .[!.][!.git]* and the like, but none refer to the intended files.

dotancohen
  • 15,864

4 Answers4

7

You can use the extended globbing in bash:

shopt -s extglob
ls .!(.|git)

This also matches ., though, so you probably need

ls .!(|.|git)
karel
  • 2,030
choroba
  • 47,233
2

You can use find :

find . -type f '!' -iname ".git" -exec cp -rv {} /dest/path  \;

It will search all files in current directory and but not include .git as we used ! -iname ( where ! means not equal to) then it will copy file to destination location

Update

find . -not -path '.' -not -path './.git' -iname '.*'

also we can use -iregex in find

find . -not -iregex '.\|./.git' -iname '.*'

both example will refer to all dotfiles except for .. and .git in current path

Rahul Patil
  • 24,711
  • Thanks. This does not work, and it took me a long time to figure out why. It turns out that -iname relates only to the name of the file, not to any of the names of directories in the files' paths. – dotancohen Sep 17 '13 at 10:29
  • I thought .git was file, and I've also tested with .git as file.. – Rahul Patil Sep 19 '13 at 10:18
  • -not is a non-standard extension. The standard equivalent is ! which when on its own like that doesn't need to be quoted in any shell. -iname and -iregex are also non-standard extensions and the OP said nothing about preserving a .GIT or .Git file. Also beware it will still report hidden hidden files in the .git directory, and . is a regex operator so the last one will also exclude a ./agit file for instance. – Stéphane Chazelas Dec 17 '23 at 09:02
0

If you want to find all hidden files (which includes the files whose name starts with . and those in directories whose name starts with . recursively) recursively except for .git and any hidden files within, you can do:

LC_ALL=C find . -path ./.git -prune -o -path '*/.*' -print

To copy those elsewhere, you want to stop find searching inside those hidden directories:

LC_ALL=C find . -path ./.git -prune -o -name '.?*' -prune -exec sh -c '
  exec cp -Rip "$@" /path/to/destination/' {} +

(the -i option so the user be prompted when both ./foo/.file and ./bar/.file end up copied as /path/to/destination/.file).

With the GNU implementation of cp, sh can be avoided with its -t option:

LC_ALL=C find . -path ./.git -prune -o -name '.?*' -prune \
  -exec exec cp -ait /path/to/destination/ {} +

Those only skip the top-level .git directory/file. To skip any .git file, even those in sub-directories, replace -path ./.git with -name .git.

0

In zsh like in the Forsyth shell and derivatives (pdksh and derivatives such as mksh), . and .. are never included in its glob expansions, so to list .* files in the current working directory except .git, also excluding the special . and .. navigation entries on one column, you can do:

set -o extendedglob
print -rC1 .^git(N)

. and .. also excluded by default in bash since version 5.2 unless the globskipdots option is turned off. The extended glob syntax there is shaped after that of ksh88, and bash has no builtin command to print on 1 Column and the Nullglob option cannot be turned on a per-glob basis with glob qualifiers, so the equivalent would have to be something like:

println() { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
shopt -s extglob nullglob
println .!(git)

In older versions, . and .. can be included in glob expansions, unless the dotglob option is enabled and the . is not matched explicitly, so in those older versions, you can do:

println() { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
shopt -s extglob nullglob dotglob
println [.]!(git)

[.] instead of . so the match is not explicit.

Or exclude them explicitly with:

println() { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
shopt -s extglob nullglob
println .!(|.|git)

ksh93 skips . and .. by itself since ksh93u+m 2020-08-09. Since ksh93s, it also has ~(N) as an equivalent to zsh's N glob qualifier.

function println { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
println ~(N).!(git)

In older versions, you can do:

function println { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
FIGNORE=.:..
println ~(N).!(git)

Beware though that setting FIGNORE means hidden files are no longer excluded from globs, so setting it to .:.. has the same effect as setting the dotglob option in zsh or bash5.2+.

bash has a GLOBIGNORE variable as something similar to ksh's FIGNORE, but its design is pretty broken so I don't recommend using it.

Of course, you can also exclude them explicitly with:

function println { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }
println ~(N).!(git)