Toán tử so khớp 0 hoặc nhiều hơn trong lớp vỏ


9

Tôi bị mắc kẹt trong một vấn đề khá nhỏ ở đây: làm thế nào tôi có thể làm cho *biểu tượng trong bash có nghĩa là 0 hoặc nhiều hơn , giống như trong các công cụ như sed?

Ví dụ: ak*phải khớp với bất kỳ tệp nào có tên bao gồm toàn bộ atheo sau bằng 0 hoặc nhiều ks. Mở rộng của nó sẽ bao gồm a, ak, akk, và akkk, nhưng không phải akc.

Tôi đã thử unsetopt sh_globtrong zsh và set -o noglobbash; họ đã không tạo ra hành vi mong muốn.


Nó có phải là toàn cầu?
heemayl

Tôi thực sự không chắc chắn, tôi chỉ muốn chỉ ra những gì tôi đã thử. Tôi đã thay đổi tiêu đề để phản ánh điều đó.
Kira

Từ bạn đang tìm kiếm là "globalbing" hoặc "wildcards" (sau này có thể phụ thuộc vào ngữ cảnh).
phk

Ừm, regex ak*trong sedsẽ hoàn toàn khớp akc(và cũng chỉ a).
thrig

@thrig, ^ak+$hoặc ^akk*$sẽ làm việc.
cas

Câu trả lời:


10

Ngoại trừksh93 , không có trình bao thông thường nào có biểu thức chính quy có cùng cú pháp như sed, awk, v.v. có thể được sử dụng cho các tệp phù hợp.

Ksh93, bash và zsh có các biểu thức chính quy với một cú pháp khác tương thích ngược với các khối:

  • ?khớp với bất kỳ ký tự đơn nào (như .trong cú pháp regrec thông thường)
  • […] phù hợp với một bộ ký tự theo hầu hết cùng một cách
  • *(FOO)phù hợp với bất kỳ số lần xuất hiện nào FOO(như trong cú pháp regrec thông thường)(FOO)*
  • tương tự khớp với một hoặc nhiều lần xuất hiện và khớp với 0 hoặc một lần xuất hiện+(FOO)?(FOO)
  • @(FOO|BAR)phù hợp với FOOhoặcBAR
  • Các trận đấu áp dụng cho toàn bộ chuỗi, không phải là một chuỗi con; nếu bạn muốn một chuỗi con, đặt *ở đầu và cuối

Cú pháp này cần được kích hoạt bằng shopt -s extglobbash và với setopt ksh_globzsh. Vì vậy, trong bash bạn viết

shopt -s extglob
ls a*(k)

Xem thêm Tại sao biểu thức chính quy của tôi hoạt động trong X nhưng không ở Y?

Ksh93, zsh và bash có thể thực hiện khớp biểu thức chính quy với các biểu thức chính quy mở rộng (về cơ bản là cú pháp của awk) trên các chuỗi, với =~toán tử của [[ … ]]cấu trúc. Điều này không thuận tiện cho việc liệt kê các tập tin, nhưng nếu bạn thực sự muốn nó, nó có thể được thực hiện.

shopt -s dotglob  # <<< include dot files, for bash
setopt globdots   # <<< include dot files, for zsh
FIGNORE='@(.|..)' # <<< include dot files, for ksh
for x in *; do
  if [[ $x =~ ^ak*$ ]]; then
    
  fi
done

Một câu trả lời đơn giản như ls a*(k)đã giúp bạn có một câu trả lời được chấp nhận, vì vậy cảm ơn bạn đã dành thời gian để đưa ra câu trả lời thấu đáo này.
Kira

1
Bây giờ, bạn có chắc chắn rằng viên đạn thứ ba của bạn là chính xác? a(k)đưa ra lỗi cú pháp, do đó tôi cho rằng (foo) không phải là toàn bộ cú pháp cho phần đó.
Kira

1
@Kira Tôi cũng bối rối vì điều này. Tuy nhiên, nhìn vào nguồn của câu trả lời , có vẻ như Gilles có ý *(foo)hơn là (foo). Tôi đã đề xuất một chỉnh sửa để sửa chữa định dạng.
Blacklight Shining

ksh93quả cầu có thể sử dụng các biểu thức thông thường: echo ~(E:ak*)cho ERE.
Stéphane Chazelas

5

ls ak{k,}sẽ hiển thị các tập tin bắt đầu bằng aktheo sau bởi một cái khác khoặc không có gì.

$ touch ak akk akc
$ ls -l ak{k,}
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 ak
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 akk

quả cầu không phải là regexps, nhưng có nhiều thứ hơn là chỉ *?.

Nếu bạn muốn sử dụng regexps để tìm tên tệp phù hợp, bạn có thể sử dụng findlệnh:

$ find . -maxdepth 1 -type f -regex './ak+$' 
./ak
./akk

Các -maxdepth 1tùy chọn giới hạn tìm kiếm để chỉ thư mục hiện hành (không có thư mục con sẽ được tìm kiếm)

Nếu bạn muốn tìm kiếm không phân biệt chữ hoa chữ thường, hãy sử dụng -iregexchứ không phải -regex.

Có nhiều phương pháp để sử dụng các tệp được tìm thấy findtrong các lệnh khác. Ví dụ:

find . -maxdepth 1 -type f -regex './ak+$' -ls
find . -maxdepth 1 -type f -regex './ak+$' -exec ls -ld {} +
find . -maxdepth 1 -type f -regex './ak+$' -print0 | xargs -0r ls -ld
ls -ld $(find . -maxdepth 1 -type f -regex './ak+$')

Ví dụ cuối cùng thiên về các chế độ thất bại khác nhau, bao gồm 1. không đối phó với khoảng trắng, v.v. trong tên tệp, 2. giới hạn độ dài dòng lệnh. không được khuyến khích.


Tôi thực sự có ý tưởng này nhưng tôi nghĩ OP muốn một giải pháp chung chung hơn mà không bị ràng buộc với bất kỳ tên tệp nào ..
heemayl

Câu trả lời của bạn hơi quá cụ thể, tôi muốn có một số lượng tùy ý k. Tôi sẽ chỉnh sửa câu hỏi để phản ánh điều đó.
Kira

@heemayl, cũng như regexps, các khối cần phải được tạo ra cho các trường hợp chính xác theo yêu cầu. @kira, xem man 7 glob- không giống như regex, global không có công cụ sửa đổi từ 0 trở lên hoặc 1 hoặc nhiều hơn.
cas

2

Trong bashcú pháp bạn có thể sử dụng là: ls a+(k) Điều này phụ thuộc vào bash shopttùy chọn shell extglobđược bật. Trên Ubuntu 14.04 GNU / Linux, điều này dường như được bật theo mặc định.

Đây là cách nó hoạt động:

$ shopt extglob
extglob         on
$ ls
ak  akc  akd  akk  akkk  akkkk
$ ls a+(k)
ak  akk  akkk  akkkk
$ shopt -u extglob
$ shopt extglob
extglob         off
$ ls a+(k)
bash: syntax error near unexpected token `('
$

Từ Cẩm nang Bash:

+ (danh sách mẫu)

Khớp với một hoặc nhiều lần xuất hiện của các mẫu đã cho.

danh sách mẫu là danh sách một hoặc nhiều mẫu được phân tách bằng dấu '|'.

Xem điều này trong Hướng dẫn Bash tại https://www.gnu.org/software/bash/manual/bash.html#Potype-Matching .


2

Một vài tùy chọn tùy thuộc vào vỏ:

$ touch a akk aka
$ ksh -c 'echo a*(k)'
a akk
$ zsh -o kshglob -o nobareglobqual -c 'echo a*(k)'
a akk

( nobareglobqualđối với dấu vết đó (k)không được coi là vòng loại toàn cầu ở đây)

$ bash -O extglob -c 'echo a*(k)'
a akk

$ zsh -o extendedglob -c 'echo ak#'
a akk

zsh's #là tương đương với regrec *.

ksh93 cũng có thể sử dụng một số loại biểu thức chính quy trong các khối của nó:

$ ksh93 -c 'echo ~(E:ak*)' # extended RE
a akk
$ ksh93 -c 'echo ~(P:ak*)' # perl-like RE
a akk
$ ksh93 -c 'echo ~(X:ak*)' # AT&T augmented RE
a akk
$ ksh93 -c 'echo @(~(E)ak*)' # alternative syntax
a akk
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.