开发者

bash: getting rid of '.' and '..' when looping over files that begin with '.' in a folder

开发者 https://www.devze.com 2022-12-25 08:33 出处:网络
I want to loop over the files that begin with \'.\' in directory x (x mi开发者_运维问答ght be any path):

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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号