Cài đặt trong bash cho globalbing là gì, để kiểm soát xem * có khớp với các tập tin dấu chấm không


12

Gần đây tôi đã rất ngạc nhiên khi tôi làm một cái gì đó giống như mv ./* ../somedirectory và thấy rằng các tệp như .gitignorekhông được di chuyển.

Tôi làm hầu hết công việc của mình trong zsh trên OS X, và điều này làm tôi ngạc nhiên khi thấy bash trên CentOS. Tôi đã thử bash trên OS X và thấy hành vi tương tự: *không khớp với các tệp chấm. Điều này có vẻ rất không mong muốn đối với tôi, nhưng rõ ràng đó là mặc định bash. (Nó có thể là mặc định zsh cho tất cả những gì tôi nhớ, nhưng tôi có thể đã thay đổi nó nhiều năm trước trong .zshrc của tôi và quên nó từng hoạt động khác đi.)

Làm cách nào tôi có thể định cấu hình bash để hoạt động như tôi mong đợi: for * để khớp với tất cả các tệp và không bỏ qua các tệp chấm.

Trong trường hợp điều này hoàn toàn không rõ ràng, đây là cách tái tạo nó

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed


Vâng, chúng có liên quan. Điều đó đã không xuất hiện khi tôi tìm kiếm (có lẽ vì người hỏi không đề cập đến các tập tin toàn cầu hoặc dấu chấm), nhưng đó thực sự là một câu hỏi khác. Anh ấy hỏi làm thế nào để di chuyển các tập tin, và tôi đang hỏi làm thế nào để thay đổi hành vi shell, và đặc biệt cho bash. Tôi có thể không hỏi liệu câu hỏi đó có xuất hiện cho tôi không (vì câu trả lời cực kỳ đầy đủ và kỹ lưỡng của bạn có bao gồm cài đặt bash) nhưng đó vẫn là một câu hỏi khác và ai đó đang tìm câu trả lời cho câu hỏi của tôi không nhất thiết phải tìm Câu hỏi của anh ấy.
iconoclast

Toàn bộ một người nào đó đang tìm kiếm câu trả lời cho câu hỏi của tôi không nhất thiết phải tìm câu hỏi của anh ấy về kinh doanh chính xác là lý do tại sao chúng ta có cách đóng câu hỏi này như là bản sao.
Gilles 'SO- ngừng trở nên xấu xa'

vâng, nhưng dường như bạn đang thiếu điểm chính, đó là một câu hỏi khác .
iconoclast

Câu trả lời:


14

Bash

Như bạn đã nhận thấy bash sẽ không khớp với .lúc bắt đầu tên hoặc dấu gạch chéo. Để thay đổi kết quả khớp với dấu chấm, bạn phải đặt dotglobtùy chọn - man bash :

dotglob Nếu được đặt, bash bao gồm tên tệp bắt đầu bằng một `. ' trong
    kết quả mở rộng tên đường dẫn.

Để bật / đặt nó với sử dụng bash shopt, ví dụ:

shopt -s dotglob


Đối với zsh, bạn cũng có thể sử dụng dotglobtùy chọn nhưng bạn sẽ phải sử dụng setoptđể kích hoạt nó, ví dụ:

setopt dotglob

2
Với zsh, tùy chọn tốt hơn là bật dotglob trên cơ sở toàn cầu:mv test/*(D) /dest
Stéphane Chazelas

7

Tôi đã thử nghiệm điều này và nó giải quyết vấn đề:

shopt -s dotglob

Đầu ra:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....

Xin lỗi nhưng Ulrich đã trả lời trước.
iconoclast

3
Tất cả chúng ta cùng chung một đội. Ổn mà.
Jodie C

6

*là một quả địa cầu được mở rộng bởi vỏ. Theo mặc định, shell không bao gồm các tệp có tên bắt đầu bằng .(được gọi là tệp ẩn hoặc dấu chấm) trừ khi mục .nhập được nhập theo nghĩa đen.

*hoặc [.]*hoặc ?*hoặc *.*hoặc dir/*sẽ không bao gồm dotfiles.

.*hoặc dir/.*sẽ

Vì vậy, bạn có thể làm:

mv -- * .* /dest/

tuy nhiên, một số shell bao gồm bash(nhưng không zsh, mkshcũng không fish) có sự không thỏa đáng rằng việc mở rộng .*bao gồm các mục thư mục đặc biệt ...mà bạn không muốn ở đây (và nói chung không bao giờ muốn một quả cầu bao gồm đó là lý do tại sao tôi gọi nó là không phù hợp).

Vì lý do đó, bạn sẽ thấy rằng đôi khi mọi người sử dụng (trong các vỏ giống như Bourne):

mv -- * .[!.]* ..?* /dest/

Đó là ba quả cầu, cái đầu tiên khớp với các tệp không bị ẩn, tên tệp thứ hai bắt đầu .bằng một ký tự khác .và tên tệp thứ ba bắt đầu ..bằng ít nhất một ký tự.

Tuy nhiên, một số vỏ hiện đại có những cách tốt hơn xung quanh đó

zsh

Với zsh, bạn có thể sử dụng (D)vòng loại toàn cầu để chỉ định rằng toàn cầu nên bao gồm các dotfiles:

mv -- *(D) /dest/

zshcũng đã sửa lỗi không phù hợp khác của shell Bourne ở chỗ nếu mẫu không khớp, mvlệnh không được chạy.

Như đã nói ở trên, nó cũng sẽ không bao giờ bao gồm .cũng như ..trong các khối của nó, vì vậy

mv -- * .* /dest/

Sẽ được an toàn. Tuy nhiên, nếu không có tệp phù hợp *hoặc không có tệp phù hợp với .*lệnh sẽ bị hủy bỏ, vì vậy sẽ tốt hơn nếu sử dụng:

mv -- (*|.*) /dest/

Giống như trong một số shell khác, bạn cũng có thể buộc tất cả các khối được bao gồm các dotfiles (ví dụ nếu bạn thấy mình muốn các dotfile được bao gồm thường xuyên hơn không) với:

setopt dotglob

hoặc là:

set -o dotglob

Sau đó, nếu bạn muốn một quả cầu cụ thể không bao gồm dotfiles, bạn có thể viết nó:

echo *(^D)

Hoặc là:

echo [^.]*

Bash

Thật không may bash, không có vòng loại toàn cầu. Vì vậy, bạn còn lại với việc kích hoạt bao gồm dotfile trên toàn cầu. Trong bash, cú pháp là:

shopt -s dotglob

(và sử dụng [^.]*cho các khối mà không có tập tin ẩn).

Với dotglob, bashkhông bao gồm .cũng không bao gồm ..trong ảm đạm như *, nhưng vẫn làm cho ảm đạm như thế .*.

Nếu bạn đặt GLOBIGNOREbiến thành một thứ gì đó không trống, thì nó sẽ tự động kích hoạt dotglobtùy chọn và loại trừ ...từ các khối .*nhưng không phải từ dir/.*hoặc .*/filemột số (!) Để việc bảo vệ là vô ích. Bạn có thể làm GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'nhưng sau đó nó sẽ phá vỡ những đống như */.hoặc ./*hoặc ../*.

Một công việc tốt hơn là sử dụng [.]*hoặc dir/[.]*hoặc [.]*/file(có dotglobbật) để mở rộng các dotfiles ngoại trừ ....

fishquả cầu không bao gồm .cũng không ... Khi không có kết quả khớp, tùy thuộc vào phiên bản, nó sẽ hoạt động như zsh(hoặc bash -o failglob) hoặc bash -o nullglob.

mv -- * .* /dest/

Sẽ hoạt động nếu có cả tệp ẩn và tệp không ẩn. Mặt khác, YMMV và với một số phiên bản, nó có thể gọi mv -- /destnếu không có tệp nào cả.

ksh93

Không có vòng loại toàn cầu trong ksh93một trong hai. Bạn có thể bao gồm các dotfiles trong các khối với:

FIGNORE='@(.|..)'

Trái với bashs' GLOBIGNORE, đó là thực hiện đúng cách và cũng có thể sửa chữa vấn đề của .*bao gồm ....

yash

yashcó một dot-globtùy chọn ( set -o dot-glob), nhưng trái với bash, mở rộng toàn cầu (thậm chí *) bao gồm ...vì vậy nó khá vô dụng.

tcsh

set globdot

Hoạt động như trong bash, *bao gồm các tệp chấm trừ ...nhưng .*vẫn bao gồm ...(và bạn có thể sử dụng [.]*để mở rộng các tệp ẩn ngoại trừ ...).


4

Cách dễ nhất trong bash để in các tệp chấm phù hợp, bỏ qua ...là:

$ GLOBIGNORE=/+/
$ printf '%s\n' *


Để kiểm tra:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Nó in tất cả các tệp có dấu chấm hoặc không, ngoại trừ đáng chú ý là ....

Ví dụ của bạn cũng sẽ hoạt động chính xác:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

Làm trống các tập tin quan trọng.
Các tệp hệ thống . ..vẫn tồn tại nhưng phần mở rộng shell (sử dụng *) sẽ không bao gồm chúng.

dest thư mục ination chứa tất cả các tập tin:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Tại sao?

Này hoạt động vì thiết GLOBIGNORE có này phía tác:

  • Đặt dotglob ( shopt -s dotglob) thành các tệp chấm toán học trên các bản mở rộng.
  • Tên tệp ...được bỏ qua khi GLOBIGNORE được đặt và không null.

Chi tiết trong hướng dẫn sử dụng : LESS=+'/The GLOBIGNORE' man bash.

Ngoài ra, chúng ta cần đặt biến GLOBIGNORE để chứa một tên mà không tên tệp nào có thể khớp:
dấu gạch chéo (và một cái gì đó khác chỉ là một biện pháp phòng ngừa kép).

$ GLOBIGNORE=/+/

Cũng có thể hữu ích khi đặt nullglob nếu cần để tránh nhận được *khi không có tệp nào khớp với *.


0

Rất thích cài đặt này của GLOBIGNORE = / + / @ user79743

Theo kinh nghiệm của tôi, việc không đặt dotglob là việc kinh doanh tồi đối với hầu hết tất cả các lệnh của bạn (cp, mv, v.v.) NHƯNG điều tương tự là đúng khi đặt nullglob (đặc biệt là với các biểu thức thông thường cần thoát!).

Tuy nhiên, nếu bạn cần phải đặt chúng vào đầu chương trình nhưng can thiệp vào các phần cụ thể nơi bạn gọi các lệnh như cp, mv, v.v. hoặc sử dụng các biểu thức thông thường chỉ cần làm điều này:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.