Điểm:6

Làm cách nào để kiểm tra một chuỗi cụ thể có ngắt dòng trong một tệp có grep?

lá cờ cn

Tôi có một biến chuỗi trong tệp tập lệnh bash như sau:

chuỗi = "

kiểm tra1

kiểm tra2

"

và tôi muốn kiểm tra xem một tập tin test.txt ngăn chặn chuỗi cụ thể này (bao gồm cả ngắt dòng. Tức là nó sẽ thất bại nếu nó chỉ chứa những điều sau đây:

đây là một bài kiểm tra:
kiểm tra1

kiểm tra2
và một cái khác

bởi vì không có ngắt dòng ở trên test1 và bên dưới test2.

(Lý do tôi muốn kiểm tra điều này là vì tôi muốn kiểm tra xem một đoạn mã nhất định có trong tệp nguồn hay không và nếu không, hãy thêm nó vào.)


Những điều sau đây không hoạt động:

chuỗi = "
    
    kiểm tra1
    
    kiểm tra2
    
    "
nếu ! chuỗi grep -q "test.txt"; sau đó
    tiếng vang "$string" >> test.txt
fi

Thao tác này sẽ thêm chuỗi vào tệp một cách chính xác, nhưng nó thực hiện ngay cả khi chuỗi đã được thêm vào. Ngoài ra, nó hoạt động chính xác khi tôi thay đổi chuỗi thành không có ngắt dòng.


CHỈNH SỬA:

Các câu trả lời của @terdon và @steeldriver bên dưới phù hợp với ví dụ về chuỗi mà tôi đã viết ở trên, nhưng vì lý do nào đó mà chúng bị hỏng đối với ví dụ thực tế hơn này:

chuỗi = "                                                                
                                                               
nếu [ -f ~/.script ]; sau đó                            
        . ~/.script         
fi

"  
user56834 avatar
lá cờ cn
@Terrance, xin lỗi bỏ qua nhận xét trước đây của tôi. Nó thực sự vẫn không hoạt động, nhưng lỗi thì ngược lại: bây giờ nó không bao giờ điều chỉnh tệp, ngay cả khi chuỗi không có ở đó ngay từ đầu. (Vì vậy, nếu tôi thực thi nó 5 lần, thay vì kết thúc với 5 bản sao như tôi đã làm với mã gốc của mình, thì tôi sẽ kết thúc bằng 0, trong khi tôi nên kết thúc bằng 1).
terdon avatar
lá cờ cn
Vâng vâng. Đó là một tình huống hoàn toàn khác, bạn đang sử dụng tất cả các loại ký tự đặc biệt. Vui lòng [chỉnh sửa] câu hỏi của bạn và thêm i) chính xác những gì bạn đang làm, bạn đang sử dụng phương pháp nào; ii) cách bạn gọi tập lệnh của mình và iii) bạn gặp phải lỗi gì (việc cho chúng tôi biết tập lệnh bị lỗi không giúp chúng tôi hiểu được).
user56834 avatar
lá cờ cn
@terdon, xin lỗi, vâng, tin nhắn của tôi không rõ ràng lắm. i) tôi đã sử dụng cả appraoch và @steeldiver của bạn. Ví dụ.từ cách tiếp cận của bạn, tôi chỉ thay đổi định nghĩa của `string` ii) Tôi đang gọi nó bằng "bash substtest.sh" và iii) nó không báo lỗi, thay vào đó, nó sẽ thêm văn bản chuỗi vô thời hạn nếu tôi gọi bash substtest .sh nhiều lần, thay vì chỉ thêm một lần.
terdon avatar
lá cờ cn
Bạn đang chạy lệnh nào mà không thành công? Làm thế nào bạn điều chỉnh câu trả lời của tôi để phù hợp với dữ liệu thực tế của bạn? Đây là một tình huống hoàn toàn khác với câu hỏi ban đầu của bạn. "Chuỗi" bạn đang tìm có chứa các ký tự đặc biệt. Bạn sẽ cần một cái gì đó như `string='\n\nif \[ -f ~/.script \]; sau đó\s*\n\s*\. ~/\.script\s*\nfi\n\n'`.
terdon avatar
lá cờ cn
Xem câu trả lời cập nhật.
Điểm:6
lá cờ cn

Vấn đề là ở đó tiếng kêu sẽ chạy trên từng dòng chứ không phải toàn bộ tệp. Miễn là tệp đủ nhỏ để vừa với bộ nhớ (điều này sẽ xảy ra trong phần lớn các tình huống ngày nay), bạn có thể sử dụng grep's -z cờ để nhét toàn bộ tệp:

-z, --null-dữ liệu Xử lý dữ liệu đầu vào và đầu ra dưới dạng chuỗi các dòng, mỗi dòng kết thúc bằng một byte không (mã ASCII NUL ký tự) thay vì một dòng mới. Giống như tùy chọn -Z hoặc --null, tùy chọn này có thể được sử dụng với các lệnh như sort -z để xử lý các tên tệp tùy ý.

Vấn đề tiếp theo, là nếu bạn vượt qua tiếng kêu một cái gì đó có dòng mới, nó sẽ coi nó như một danh sách các mẫu để grep:

$chuỗi="1
> 2"

$ tiếp theo 10 | grep "$string"
1
2
10
"

Điều đó có nghĩa là tôi e rằng bạn sẽ phải diễn đạt mẫu dưới dạng một biểu thức chính quy thích hợp:

\n\ntest1\n\ntest2\n\n

Tuy nhiên, điều này cũng có nghĩa là bạn cần -P để kích hoạt các biểu thức chính quy tương thích với perl để \N sẽ làm việc.

Tôi đã tạo hai tệp này để chứng minh:

$ mèo tệp1
đây là một bài kiểm tra:
kiểm tra1

kiểm tra2
và một cái khác

$ mèo tệp2
đây là một bài kiểm tra:

kiểm tra1

kiểm tra2

và một cái khác

Sử dụng hai tệp đó và thông tin ở trên, bạn có thể thực hiện:

$ grep -Pz '\n\ntest1\n\ntest2\n\n' file1
$ 

$ grep -Pz '\n\ntest1\n\ntest2\n\n' file2
đây là một bài kiểm tra:

kiểm tra1

kiểm tra2

và một cái khác

Đặt tất cả những điều này lại với nhau mang lại cho chúng ta:

chuỗi='\n\ntest1\n\ntest2\n\n'
nếu ! grep -Pzq "$string" test.txt; sau đó
    printf "$string" >> test.txt
fi

Hoặc, như được đề xuất bởi @steeldriver trong một nhận xét, bạn có thể sử dụng một biến và chuyển đổi các dòng mới thành \N một cách nhanh chóng:

$chuỗi="

    kiểm tra1

    kiểm tra2

    "
$ nếu ! grep -Pzq "${string//$'\n'/\n}" test.txt; sau đó
    printf "$string" >> test.txt
fi

Nếu chuỗi của bạn chứa các ký tự đặc biệt có ý nghĩa trong các biểu thức thông thường, như bạn hiện đang hiển thị trong câu hỏi được cập nhật của mình, thì đó là một tình huống hoàn toàn khác. Đối với ví dụ bạn hiển thị, bạn sẽ cần thứ gì đó phức tạp hơn nhiều. Như thế này:

searchString='\n\nif \[ -f ~/.script \]; sau đó\s*\n\s*\.\s+~/\.script\s*\nfi\n\n'
printString='
nếu [ -f ~/.script ]; sau đó
   . ~/.script         
fi

'
nếu ! grep -Pzq "$searchString" test.txt; sau đó     
    printf "%s" "$printString" >> test.txt 
fi
user56834 avatar
lá cờ cn
Cảm ơn! Tôi cho rằng bạn có nghĩa là 'nếu! grep -q -z "$string" "test.txt"; sau đó`, tức là có thêm -z?
user56834 avatar
lá cờ cn
Trên thực tế, ngay cả khi thêm -z, tôi vẫn gặp vấn đề tương tự như tôi đã nêu trong nhận xét cho câu hỏi ban đầu của mình: Đó là, với một trong hai `if ! grep -q -z "$string" "test.txt"; sau đó` hoặc `nếu ! grep -q "$string" "test.txt"; sau đó` hoặc `nếu ! grep -q -z "$string" test.txt; then`, nó thất bại theo một cách khá kỳ lạ:
terdon avatar
lá cờ cn
@ user56834 rất tiếc, vâng. Nhưng điều này sẽ không thực sự hoạt động với một biến. Hãy cho tôi một vài phút, tôi đang cố gắng tìm ra vấn đề.
Terrance avatar
lá cờ id
Mát mẻ! +1 Có khoảng trắng trong chuỗi dưới dạng `string='\n\n test1\n\n test2\n\n'` cũng hoạt động tốt. :)
terdon avatar
lá cờ cn
@ user56834 vui lòng xem câu trả lời được cập nhật.
terdon avatar
lá cờ cn
@steeldriver duh! Cảm ơn, tôi có thể đã thề tôi đã làm. Nhưng không, tôi chỉ thử nghiệm nó trong một thiết bị đầu cuối và quên mất. Đã sửa ngay bây giờ, cảm ơn.
user56834 avatar
lá cờ cn
Xin lỗi vì sự chậm trễ, và cảm ơn! Về đề xuất của steeldriver, thật kỳ lạ là tôi gặp lỗi: "substtest.sh 12: Bad substitution"
user56834 avatar
lá cờ cn
Ồ, đừng bận tâm, có vẻ như điều này đã được giải quyết bằng cách thực thi tập lệnh .sh bằng bash thay vì sh (dấu gạch ngang). Không chắc chắn lý do tại sao. Nó hoạt động! tuyệt vời. (Mặc dù tôi không hiểu phần "//$'\n'/\n}". Có lời giải thích hay về điều này không?)
terdon avatar
lá cờ cn
@ user56834 `dash` và `sh` _not_ `bash` và không được coi là đồng nghĩa. Dash là shell POSIX tối thiểu và thiếu nhiều tính năng của shell `bash` phức tạp hơn. Tương tự với `sh`. Đối với `"${string//$'\n'/\n}"`, đó là sự thay thế (dành riêng cho bash). Định dạng chung là `${var//old/new}` sẽ thay thế tất cả các lần xuất hiện của `old` bằng `new` trong biến `$var`.Ở đây, "cũ" là `$'\n'`, đây là một cách chuyển một dòng mới tới trình bao.
user56834 avatar
lá cờ cn
Trên thực tế, tôi vừa thử điều tương tự với một trường hợp phức tạp hơn và điều này khiến nó bị hỏng. Xem câu hỏi ban đầu của tôi.
Điểm:4
lá cờ hr

Bạn có thể muốn xem xét sử dụng pcregrep với -M hoặc --đa dòng tùy chọn để cho phép khớp các dòng mới theo nghĩa đen:

   -M, --multiline
             Cho phép các mẫu khớp với nhiều dòng. Khi tùy chọn này
             được đưa ra, các mẫu có thể chứa ký tự xuống dòng theo nghĩa đen một cách hữu ích
             các tác nhân và sự xuất hiện bên trong của các ký tự ^ và $.

Bán tại. được cho

$ mèo test.txt
đây là một bài kiểm tra:
kiểm tra1

kiểm tra2
và một cái khác


    kiểm tra1

    kiểm tra2
    
    

$ mèo test2.txt
đây là một bài kiểm tra:
kiểm tra1

kiểm tra2
và một cái khác


    kiểm tra3

    kiểm tra4
    
    

với

$chuỗi="

    kiểm tra1

    kiểm tra2

    "

sau đó

$ pcregrep -qM "$string" test.txt && echo 'đã tìm thấy' || tiếng vang 'không tìm thấy'
thành lập

$ pcregrep -qM "$string" test2.txt && echo 'đã tìm thấy' || tiếng vang 'không tìm thấy'
không tìm thấy
user56834 avatar
lá cờ cn
Cảm ơn, điều này hoạt động. Thật không may, nó không thành công đối với một ví dụ thực tế hơn mà tôi đã thêm vào câu hỏi của mình (chỉ là câu trả lời của sterdon không thành công trong ví dụ đó)
lá cờ hr
@ user56834 điều đó có thể là do `[ ... ]` biểu thị một phạm vi ký tự trong PCRE. Hãy thử thay thế `"$string"` bằng `"\Q${string}\E"`
user56834 avatar
lá cờ cn
một lúc sau mới trả lời nhưng: Bạn có thể chỉ cho tôi một nơi mà tôi có thể đọc về những gì \Q và \E làm không?
lá cờ hr
@ user56834 hãy thử [quotemeta] của perldoc(https://perldoc.perl.org/functions/quotemeta)
Điểm:2
lá cờ cn

Tìm kiếm các mẫu nhiều dòng trong một tệp có thể dễ dàng hơn với awk:

tên tệp awk '/Mẫu bắt đầu/,/mẫu kết thúc/'

Kiểm tra bài này để biết thêm chi tiết

Đăng câu trả lời

Hầu hết mọi người không hiểu rằng việc đặt nhiều câu hỏi sẽ mở ra cơ hội học hỏi và cải thiện mối quan hệ giữa các cá nhân. Ví dụ, trong các nghiên cứu của Alison, mặc dù mọi người có thể nhớ chính xác có bao nhiêu câu hỏi đã được đặt ra trong các cuộc trò chuyện của họ, nhưng họ không trực giác nhận ra mối liên hệ giữa câu hỏi và sự yêu thích. Qua bốn nghiên cứu, trong đó những người tham gia tự tham gia vào các cuộc trò chuyện hoặc đọc bản ghi lại các cuộc trò chuyện của người khác, mọi người có xu hướng không nhận ra rằng việc đặt câu hỏi sẽ ảnh hưởng—hoặc đã ảnh hưởng—mức độ thân thiện giữa những người đối thoại.