Grep cho mẫu ở đầu hoặc giữa của một dòng


9

Tôi sẽ bắt đầu bằng cách nói rằng tôi nghĩ vấn đề này hơi ngây thơ hơn âm thanh.

Những gì tôi cần làm: kiểm tra một thư mục trong biến môi trường PATH. Nó có thể là lúc bắt đầu hoặc một nơi nào đó sau. Tôi chỉ cần xác minh rằng thư mục đó là ở đó.

Ví dụ về vấn đề của tôi - hãy sử dụng /opt/gnome.


SCENARIO 1: thư mục không nằm ở đầu PATH

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Lưu ý rằng grep cần phải đủ cụ thể để nó không bắt được /var/opt/gnome. Do đó đại tràng.


SCENARIO 2: thư mục nằm ở đầu PATH.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Đây là vấn đề của tôi - tôi cần tìm kiếm dấu hai chấm hoặc bắt đầu với thư mục này. Những gì tôi muốn làm là một trong hai biểu thức khung này:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

NHƯNG [^[:có ý nghĩa riêng của họ. Do đó, hai lệnh trên không hoạt động.

Có cách nào tôi có thể grep cho hai kịch bản này trong một lệnh không?


Lưu ý rằng nhận xét của Gilles về câu trả lời của Costas cũng áp dụng cho câu hỏi: vì bạn không tham gia /opt/gnome:hoặc /opt/gnome$, bạn sẽ tìm thấy /opt/gnome-foohoặc /opt/gnome/bar.
Scott

@ Hủy bỏ - Miễn là bạn bao gồm trong trận đấu của bạn không gian can thiệp, bạn luôn có thể neo bất kỳ chuỗi nào vào đầu và đuôi của dòng mà không có các biến chứng như vậy. Giống nhưgrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeerv

Câu trả lời:


10

Nếu bạn đang kiểm tra nội dung của PATHbiến môi trường, trái ngược với việc tìm kiếm thứ gì đó trong tệp, thì đó greplà công cụ sai. Nó dễ dàng hơn (và nhanh hơn và dễ đọc hơn) để làm điều đó trong vỏ.

Trong bash, ksh và zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

Có thể:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Lưu ý việc sử dụng :$PATH:chứ không phải $PATH; bằng cách này, thành phần luôn được bao quanh bởi các dấu hai chấm trong chuỗi tìm kiếm ngay cả khi nó ở đầu hoặc cuối $PATH.

Nếu bạn đang tìm kiếm trong một dòng của một tệp, thì bạn có thể sử dụng biểu thức chính mở rộng (nghĩa là yêu cầu grep -E) (^|:)/opt/gnome($|:)để khớp /opt/gnomenhưng chỉ khi nó ở đầu dòng hoặc theo dấu hai chấm, và chỉ khi nó ở cuối dòng dòng hoặc theo sau là một dấu hai chấm.


8

Bạn có thể sử dụng các biểu thức chính quy mở rộng bằng cách chỉ sử dụng grep -E

Bạn phải khớp phần đầu và phần cuối của con đường bạn đang cố gắng tìm nếu bạn muốn tránh những thông tin sai lệch.

Khớp với thể hiện ở đầu:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Cũng phù hợp với ví dụ ở giữa:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Tránh dương tính giả:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Không có trận đấu ở đó.

Nhỏ gọn và thanh lịch. Đã thử nghiệm trên Debian 7.


1
egrepkhông được sử dụng grep -E(nguồn man grep:)
Anthon

Cảm ơn, làm việc như một nét duyên dáng! Tôi đã không chọn nó làm câu trả lời, vì tôi nghĩ tùy chọn -w đơn giản hơn một chút. Thậm chí đơn giản hơn tôi tưởng tượng ban đầu!
JamesL

3
Cảnh báo. Các -wtùy chọn có một số vấn đề. Chỉ các chữ số, chữ cái và dấu gạch dưới được coi là "từ". Vì vậy, một số ký tự bất thường nhưng có thể sẽ làm cho nó thất bại. Ví dụ echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Những người cung cấp kết quả sai.
Luis Antolín Cano

1
Bạn đang đi đúng hướng, nhưng vẫn có những điểm tích cực sai : /opt/gnome/somethingelse.
Gilles 'SO- ngừng trở nên xấu xa'

1
Hoàn toàn đúng. Chúng ta nên quan tâm đến kết thúc một cách rõ ràng, không chỉ là bắt đầu. Tôi nghĩ rằng điều này khắc phục vấn đề echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Chỉnh sửa câu trả lời.
Luis Antolín Cano

7

Nếu bạn chưa kết hôn grep, bạn có thể sử dụng awkvà phân tách các bản ghi trên:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'

5

Bạn cũng có thể sử dụng

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

phân tách biến đường dẫn thành các dòng riêng biệt (một trên mỗi đường dẫn), do đó grep -xcó thể tìm kiếm kết quả chính xác. Điều này tất nhiên có nhược điểm mà nó cần một quá trình bổ sung cho tr. Và nó sẽ không hoạt động khi một tên thư mục PATHchứa các ký tự dòng mới.


2

Tôi không biết là đủ để trả lời nhưng

grep -w "/opt/gnome"

sẽ đáp ứng nhu cầu của bạn.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

nhưng

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome

Điều này hoạt động hoàn hảo bởi vì dấu hai chấm là ký tự không từ. Cảm ơn!
JamesL

@ Sman865 Có một lý do khác: bởi vì /không phải là một phần của từ mà rlà.
Costas

2
Cảnh báo. Như tôi đã nói ở phần bình luận về câu trả lời của tôi. Có các ký tự hợp pháp cho một tên thư mục là ký tự không từ. Điều đó dẫn đến kết quả sai. Nó không phải là bình thường để kết thúc một tên thư mục trong - nhưng nó có thể xảy ra.
Luis Antolín Cano

4
@ Sman865 nhầm: /opt/gnome-beta, /home/bob/opt/gnome, ...
Gilles 'Somali dừng tà ác'

Trường hợp không hoạt động: grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games------/usr/local /usr/local /usr/local
pabouk

0

Để chọn /opt/gnomeđược bao quanh bởi các ký tự không-word (đường mới, :, /, vv) thử cái này:

grep '\B/opt/gnome'

0

Bạn có thể làm điều này một cách đáng tin cậy và với ít nỗ lực trong grep. Bạn có thể tận dụng các tiện ích mở rộng có sẵn rộng rãi và trong số đó có nhiều giải pháp đã được cung cấp, nhưng ngay cả với regex cơ bản, nó vẫn dễ dàng thực hiện, mặc dù có thể không trực quan nên ngay từ cái nhìn đầu tiên.

Với regex cơ bản - và như vậy với grep- bạn luôn có hai neo đáng tin cậy - đầu và đuôi của dòng. Bạn có thể neo một trận đấu cho cả hai điều này bất kể vị trí của nó trên dòng như:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

grepsẽ khớp từ đầu của dòng như nhiều lần xuất hiện của các \(grouped\)biểu hiện phụ vì nó phải gặp tiếp theo dấu phân cách của bạn sau đó khớp rõ ràng của bạn và từ đuôi của trận đấu của bạn đến đuôi của dòng theo cùng một cách. Nếu trận đấu rõ ràng của bạn không được kết hợp rõ ràng, nó sẽ thất bại và không in được gì.

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

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Xem cho chính mình:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

ĐẦU RA

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome

0

bạn đã nhận thấy một trường hợp cạnh ... bạn có thể tránh nó bằng cách buộc xuất hiện một: ở đầu dòng:

 echo ":$PATH" | grep ":/opt/gnome"

hoặc nếu đường dẫn chính xác, hãy thêm một ở cuối để đảm bảo nó bị chặn:

 echo ":${PATH}:" | grep ":/opt/gnome:"
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.