grepping một chuỗi cố định ở đầu một dòng


20

grep "^$1"sắp xếp các tác phẩm, nhưng làm thế nào để tôi thoát "$1"để grep không diễn giải bất kỳ nhân vật nào trong đó?

đây có phải là cách tốt hơn không?

Chỉnh sửa: Tôi không muốn tìm kiếm '^$1'nhưng đối với một chuỗi cố định được chèn động mà chỉ nên khớp với nó ở đầu dòng. Đó là những gì tôi muốn nói $1.


Bạn đã thử sử dụng dấu ngoặc đơn thay vì dấu ngoặc kép, vd grep '^$1'? Hoặc bạn không có nghĩa là bạn muốn ngăn chặn sự $1mở rộng của vỏ?
mnille

@mnille Tôi không muốn tìm kiếm '^ $ 1' nhưng đối với một chuỗi cố định được chèn động chỉ nên được khớp nếu nó ở đầu dòng. Đó là những gì tôi muốn nói với $ 1.
PSkocik

3
Bạn có thể làm điều đó với grepquá nhưng bạn sẽ phải thoát khỏi bất kỳ ký tự đặc biệt trong chuỗi ví dụ đầu tiên của bạnprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
don_crissti

@don_crissti điều đó tốt hơn một số câu trả lời khác. Quan tâm để làm cho nó một?
roaima

@roaima - Tôi biết nhưng đã có một loạt các câu trả lời ở đây và điều này (thoát khỏi các ký tự đặc biệt trong vars) là điều mà tôi (và một vài người dùng khác ở đây) đã đánh võng khá lâu ... Bạn luôn có thể thêm câu trả lời của bạn nếu bạn muốn và tôi sẽ xóa nhận xét ở đây (đừng quên thêm dấu ngoặc kép bị thiếu).
don_crissti

Câu trả lời:


7

Tôi không thể nghĩ ra một cách để làm điều này bằng cách sử dụng grep; ^chính nó là một phần của biểu thức chính quy nên việc sử dụng nó đòi hỏi các biểu thức chính quy phải được diễn giải. Đó là tầm thường sử dụng substring khớp trong awk, perlhoặc bất cứ điều gì:

awk -v search="$1" 'substr($0, 1, length(search)) == search { print }'

Để xử lý các chuỗi tìm kiếm có chứa \, bạn có thể sử dụng thủ thuật tương tự như trong câu trả lời của 123 :

search="$1" awk 'substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }'

Điều này sẽ không hoạt động đối với các chuỗi như\/
123

@ 123 thực sự, tôi đã thêm một biến thể để xử lý đó.
Stephen Kitt

Vẫn sẽ thất bại đối với các chuỗi phức tạp như \\\/\/\/\\\\/được xem như \\///\\/trong chương trình. Theo như tôi biết, không có cách nào để thoát khỏi dấu gạch chéo ngược trong awk, trừ khi bạn biết có bao nhiêu sẽ được sử dụng trước đó.
123

1
@ 123 cảm ơn, tôi đã điều chỉnh thủ thuật của bạn khi đi qua môi trường để tránh xử lý thoát.
Stephen Kitt

Tôi vẫn thích giải pháp này tốt nhất. Hiệu quả (awk + không lãng phí thời gian tìm kiếm xung quanh), khởi động nhanh (awk + không cần thêm quy trình để thiết lập trạng thái) sử dụng các công cụ tiêu chuẩn và khá súc tích. Tất cả các câu trả lời khác thiếu ít nhất một số trong số này. (Hiệu quả là một điểm mạnh ở đây vì grep được biết đến với tốc độ không thể so sánh được.)
PSkocik

14

Nếu bạn chỉ cần kiểm tra xem có tìm thấy kết quả khớp hay không, hãy cắt tất cả các dòng đầu vào theo độ dài của tiền tố mong muốn ($1 ) và sau đó sử dụng grep mẫu cố định:

if cut -c 1-"${#1}" | grep -qF "$1"; then
    echo "found"
else
    echo "not found"
fi

Cũng dễ dàng để có được số lượng các dòng phù hợp:

cut -c 1-"${#1}" | grep -cF "$1"

Hoặc số dòng của tất cả các dòng khớp (số dòng bắt đầu bằng 1):

cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1

Bạn có thể cung cấp số dòng cho headtailđể có được toàn bộ văn bản của các dòng phù hợp, nhưng tại thời điểm đó, việc tiếp cận với một ngôn ngữ kịch bản hiện đại như Python hoặc Ruby sẽ dễ dàng hơn.

(Các ví dụ trên giả định Posix grep và cắt. Họ giả sử tệp để tìm kiếm xuất phát từ đầu vào tiêu chuẩn, nhưng có thể dễ dàng điều chỉnh để lấy tên tệp thay thế.)

Chỉnh sửa: Bạn cũng nên đảm bảo rằng mẫu ( $1) không phải là chuỗi có độ dài bằng không. Nếu không thì cutnói values may not include zero. Ngoài ra, nếu sử dụng Bash, hãy sử dụng set -o pipefailđể bắt lỗi thoát cut.


10

Một cách sử dụng perl sẽ tôn trọng dấu gạch chéo ngược

v="$1" perl -ne 'print if index($_, $ENV{"v"} )==0' file

Điều này đặt biến môi trường v cho lệnh, sau đó in nếu chỉ số của biến là 0 tức là bắt đầu của dòng.

Bạn cũng có thể làm giống hệt nhau trong awk

v="$1" awk 'index($0, ENVIRON["v"])==1' file

7

Đây là một tùy chọn bash, không phải tôi khuyên dùng bash để xử lý văn bản, nhưng nó hoạt động.

#!/usr/bin/env bash
# searches for $1 at the beginning of the line of its input

len=${#1}
while IFS= read -r line
do
  [[ "${line:0:len}" = "$1" ]] && printf "%s\n" "$line"
done

Kịch bản tính toán độ dài lencủa tham số được nhập $ 1, sau đó sử dụng mở rộng tham số trên mỗi dòng để xem các lenký tự đầu tiên có khớp với $ 1 hay không. Nếu vậy, nó in dòng.


4

Nếu bạn $1là ASCII thuần túy và bạn grep-Ptùy chọn (để bật PCRE), bạn có thể thực hiện việc này:

#!/bin/bash

line_start="$1"
line_start_raw=$(printf '%s' "$line_start" | od -v -t x1 -An)
line_start_hex=$(printf '\\x%s' $line_start_raw)
grep -P "^$line_start_hex"

Ý tưởng ở đây là grep -Pcho phép các biểu thức chính quy \xXXchỉ định các ký tự bằng chữ, trong đó XXgiá trị ASCII hex của ký tự đó. Nhân vật được kết hợp theo đúng nghĩa đen, ngay cả khi đó là một nhân vật regex đặc biệt.

odđược sử dụng để chuyển đổi dòng bắt đầu dự kiến ​​thành một danh sách các giá trị hex, sau đó được xâu chuỗi lại với nhau, mỗi tiền tố \xđược in bằng printf. ^sau đó được thêm chuỗi này để xây dựng regex cần thiết.


Nếu bạn $1là unicode, thì điều này trở nên khó hơn một chút, bởi vì không có sự tương ứng 1: 1 của các ký tự với các byte hex như đầu ra od.


3

Là một bộ lọc:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern

Chạy trên một hoặc nhiều tệp:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern file..

Phần trích dẫn siêu dữ liệu của phần trích dẫn của phần mềm trong phần tài liệu perlre giải thích:

Trích dẫn siêu nhân vật

Metacharacters Backslashed trong Perl là chữ và số, chẳng hạn như \b, \w, \n. Không giống như một số ngôn ngữ biểu thức thông thường khác, không có ký hiệu gạch chéo nào không phải là chữ và số. Vì vậy, bất cứ điều gì giống như \\, \(, \), \[, \], \{, hoặc \}luôn luôn hiểu như là một ký tự chữ, không phải là một metacharater. Điều này đã từng được sử dụng trong một thành ngữ phổ biến để vô hiệu hóa hoặc trích dẫn ý nghĩa đặc biệt của siêu ký tự biểu thức chính quy trong một chuỗi mà bạn muốn sử dụng cho một mẫu. Đơn giản chỉ cần trích dẫn tất cả các ký tự không phải là tiếng Nhật:

    $pattern =~ s/(\W)/\\$1/g;

(Nếu use localeđược đặt, thì điều này phụ thuộc vào ngôn ngữ hiện tại.) Ngày nay, việc sử dụng quotemetachức năng hoặc \Q chuỗi thoát siêu dữ liệu để vô hiệu hóa tất cả các ý nghĩa đặc biệt của siêu nhân vật như thế này là phổ biến hơn :

    /$unquoted\Q$quoted\E$unquoted/

Xin lưu ý rằng nếu bạn đặt dấu gạch chéo ngược theo nghĩa đen (những biến không nằm trong các biến nội suy) giữa \Q\E, phép nội suy dấu gạch chéo kép có thể dẫn đến kết quả khó hiểu. Nếu bạn cần sử dụng dấu gạch chéo ngược bên trong \Q...\E, hãy tham khảo chi tiết của Gory về phân tích cú pháp các cấu trúc được trích dẫn trong phần perlop .

quotemeta\Qđược mô tả đầy đủ trong trích dẫn .


3

Nếu grep của bạn có tùy chọn -P, có nghĩa là PCRE , bạn có thể làm điều này:

grep -P "^\Q$1\E"

Tham khảo câu hỏi này và xem tài liệu PCRE để biết chi tiết nếu bạn muốn.


2

Nếu có một ký tự mà bạn không sử dụng, bạn có thể sử dụng ký tự đó để đánh dấu phần đầu của dòng. Ví dụ: $'\a'(ASCII 007). Nó xấu nhưng nó sẽ hoạt động:

{ echo 'this is a line to match'; echo 'but this is not'; } >file.txt

stuffing=$'\a'    # Guaranteed never to appear in your source text
required='this'   # What we want to match that beginning of a line

match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//")

if [[ -n "$match" ]]
then
    echo "Yay. We have a match: $match"
fi

Nếu bạn không cần (các) dòng trùng khớp thì bạn có thể bỏ dấu sedvà sử dụng grep -qF. Nhưng nó dễ dàng hơn nhiều với awk(hoặc perl) ...


0

Khi bạn muốn tìm trong một tệp không có vòng lặp, bạn có thể sử dụng:
Cắt tệp có độ dài của chuỗi tìm kiếm

  cut -c1-${#1} < file

Tìm chuỗi cố định và số dòng trả về

  grep -Fn "$1" <(cut -c1-${#1} < file)

Sử dụng số dòng cho một cái gì đó như sed -n '3p;11p' file

  sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/p;/' | tr -d '\n')" file

Khi bạn muốn xóa những dòng này, sử dụng

  sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/d;/' | tr -d '\n')" file
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.