bash: getting rid of '.' and '..' when looping over files that begin with '.' in a folder
I want to loop over the files that begin with '.' in directory x (x mi开发者_运维问答ght be any path):
$ for file in x/.*
> do
> echo -n $file" "
> done
x/. x/.. x/..a x/.b
What is the best way to get rid of x/., and x/.. ?
with bash, you can use GLOBIGNORE
$ ls -1tra
..
.test1
.test
test
.
$ for i in .*; do echo $i;done
.
..
.test
.test1
$ GLOBIGNORE=".:.."
$ for i in .*; do echo $i;done
.test
.test1
Use the pattern .{[^.]*,.?*}
, which means any name that starts with .
followed by a character that isn’t a .
, or a name that starts with ..
followed by a character.
Also you should write "$file"
in quotes so names with spaces don’t get broken apart.
As another pointed out, ls -A excludes . and .. from the listing, not sure how standard it is.
You can also use find:
find . -mindepth 1 -maxdepth 1 -name '.*'
A solution with only globbing, so you don't have to trouble with command substitution and quoting:
.!(.|)
This requires the extglob shell option to be set:
shopt -s extglob
This is probably obvious, but the !()
construct means anything besides one of the patterns listed (delimited by |
).
for file in x/.*; do echo "$file "; done | grep -v "x/\.\{1,2\} $" | tr -d '\n'
The space after $file
and before $
in grep
are important. It makes sure that you have spaces between the listings and that everything is not scrunched up.
or more generically
for file in somedir/.*; do echo "$file "; done | grep -v "\w/\.\{1,2\} $" | tr -d '\n'
Use ls -A
on Mac OS X. Check the man page for your implementation for local options. This command
ls -A | grep '^\.'
Gave me the list you're looking for.
for file in x/.{[^.],.?}*
This will match e.g. .a .zoo.17 ... ..0 ..hello
but will not match . .. files-not-starting-with-dot
breaking it down...
the {a,b} does an alternation, first matching a, then matching b. So we could also write the above like so:
for file in x/.[^.]* x/..?*
So let's examine the first: .[^.]* The [^.] is an inverted character class -- without the carat ^ it would match only a dot; the ^ inverts it so it will match any character except dot. After that is a star, which will match 0 or more characters of any sort So this pattern .[^.]* will match files with names that - start with . - are at least two characters long - the second character is not . From the above examples, it matches: .a .zoo.17
Now let's examine the second: ..?* The ? matches any character (but not 0 characters). We're familiar with the star. So this pattern ..?* will match files with names that - start with .. - are at least three characters long From the above examples, it matches: ... ..0 ..hello
Because of the way the alternation works, the files in the first group will all be matched first -- they will not be collated with the files in the second group.
(1) Use 2 patterns: $ echo x/.??* x/.[^.] - there are other combinations. If using a different one, make sure it would catch "..." and ".a"
(2) Use GLOBIGNORE and exclude the 2 entries: $ GLOBIGNORE='*/.:*/..'; echo x/.*
(3) Use dotglob with GLOBIGNORE. This will automatically exclude . and .., but now you have to exclude "all the rest": $ shopt -s dotglob; GLOBIGNORE='*/[^.]*'; echo x/*
Another option is to keep the globbing simple and explicitly remove the unwanted paths:
for file in x/.* ; do
[[ $file == */. || $file == */.. ]] && continue
echo -n "$file "
done
精彩评论