Score:6

จะตรวจสอบสตริงเฉพาะที่มีการแบ่งบรรทัดในไฟล์ด้วย grep ได้อย่างไร

ธง cn

ฉันมีตัวแปรสตริงในไฟล์สคริปต์ทุบตีดังนี้:

สตริง = "

ทดสอบ1

ทดสอบ2

"

และฉันต้องการตรวจสอบว่าไฟล์ ทดสอบ. txt มีสตริงเฉพาะนี้ (รวมถึงตัวแบ่งบรรทัด เช่น มันควรจะล้มเหลวหากมีเพียงสิ่งต่อไปนี้:

นี่คือการทดสอบ:
ทดสอบ1

ทดสอบ2
และอีกอันหนึ่ง

เนื่องจากไม่มีการแบ่งบรรทัดด้านบน test1 และด้านล่างของ test2

(เหตุผลที่ฉันต้องการตรวจสอบสิ่งนี้เพราะฉันต้องการตรวจสอบว่ามีโค้ดบางส่วนอยู่ในไฟล์ต้นฉบับหรือไม่ และหากไม่มี ให้เพิ่มเข้าไป)


ต่อไปนี้ใช้ไม่ได้:

สตริง = "
    
    ทดสอบ1
    
    ทดสอบ2
    
    "
ถ้า ! grep -q สตริง "test.txt"; แล้ว
    echo "$string" >> test.txt
ไฟ

สิ่งนี้จะเพิ่มสตริงลงในไฟล์อย่างถูกต้อง แต่จะเพิ่มสตริงนั้นแม้ว่าจะเพิ่มสตริงแล้วก็ตาม นอกจากนี้ยังทำงานได้อย่างถูกต้องเมื่อฉันเปลี่ยนสตริงให้ไม่มีการแบ่งบรรทัด


แก้ไข:

คำตอบโดย @terdon และ @steeldriver ด้านล่างใช้ได้กับตัวอย่างสตริงที่ฉันเขียนไว้ด้านบน แต่ด้วยเหตุผลบางประการสำหรับตัวอย่างที่สมจริงกว่านี้:

สตริง = "                                                                
                                                               
ถ้า [ -f ~/.script ]; แล้ว                            
        . ~/.script         
ไฟ

"  
user56834 avatar
cn flag
@Terrance ขออภัยที่ไม่สนใจความคิดเห็นก่อนหน้าของฉัน จริง ๆ แล้วมันยังใช้งานไม่ได้ แต่ความล้มเหลวนั้นตรงกันข้าม: ตอนนี้มันไม่เคยปรับไฟล์เลย แม้ว่าสตริงจะไม่ได้อยู่ที่นั่นตั้งแต่แรกก็ตาม (ดังนั้น ถ้าฉันรัน 5 ครั้ง แทนที่จะลงเอยด้วย 5 สำเนาเหมือนที่ฉันทำกับโค้ดต้นฉบับ ฉันจะลงเอยด้วย 0 ในขณะที่ฉันควรลงเอยด้วย 1)
terdon avatar
cn flag
ก็ใช่ นั่นเป็นสถานการณ์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิง คุณกำลังใช้อักขระพิเศษทุกประเภท โปรด [แก้ไข] คำถามของคุณและเพิ่ม i) สิ่งที่คุณกำลังทำอยู่ วิธีการที่คุณใช้; ii) วิธีที่คุณเรียกใช้สคริปต์ของคุณ และ iii) ข้อผิดพลาดใดที่คุณได้รับ (การบอกเราว่าสคริปต์หยุดทำงานไม่ได้ช่วยให้เราเข้าใจ)
user56834 avatar
cn flag
@terdon ขออภัย ใช่ ข้อความของฉันไม่ชัดเจน i) ฉันใช้ทั้ง appraoch และ @steeldiver ของคุณ เช่น.จากแนวทางของคุณ ฉันแค่เปลี่ยนคำจำกัดความของ `string` ii) ฉันเรียกมันด้วย "bash substtest.sh" และ iii) มันไม่ให้ข้อผิดพลาด แต่จะเพิ่มข้อความสตริงไปเรื่อย ๆ ถ้าฉันเรียก bash substtest .sh ซ้ำแล้วซ้ำอีก แทนที่จะเพิ่มเพียงครั้งเดียว
terdon avatar
cn flag
คุณกำลังเรียกใช้คำสั่งใดที่ล้มเหลว คุณปรับคำตอบของฉันให้พอดีกับข้อมูลจริงของคุณได้อย่างไร นี่เป็นสถานการณ์ที่แตกต่างไปจากคำถามเดิมของคุณอย่างสิ้นเชิง "สตริง" ที่คุณกำลังมองหามีอักขระพิเศษ คุณจะต้องมีบางอย่างเช่น `string='\n\nif \[ -f ~/.script \]; จากนั้น\s*\n\s*\ ~/\.script\s*\nfi\n\n'`
terdon avatar
cn flag
ดูคำตอบที่อัปเดต
Score:6
ธง cn

ปัญหาก็คือว่า เกรป จะทำงานในแต่ละบรรทัด ไม่ใช่ทั้งไฟล์ ตราบเท่าที่ไฟล์มีขนาดเล็กพอที่จะใส่หน่วยความจำได้ (ซึ่งควรเป็นกรณีส่วนใหญ่ในปัจจุบัน) คุณสามารถใช้ grep's -z ตั้งค่าสถานะเพื่อ slurp ทั้งไฟล์:

-z, --null-ข้อมูล ปฏิบัติต่อข้อมูลอินพุตและเอาต์พุตเป็นลำดับของ แต่ละบรรทัดสิ้นสุดด้วยศูนย์ไบต์ (ASCII NUL character) แทนการขึ้นบรรทัดใหม่ เช่นเดียวกับตัวเลือก -Z หรือ --null ตัวเลือกนี้สามารถใช้กับคำสั่งได้ เช่น sort -z เพื่อประมวลผลชื่อไฟล์ตามอำเภอใจ

ประเด็นต่อมาคือถ้าคุณผ่าน เกรป บางอย่างที่มีการขึ้นบรรทัดใหม่ มันจะถือว่าเป็นรายการของรูปแบบที่จะ grep สำหรับ:

$string="1
> 2"

$ seq 10 | เกรป "$string"
1
2
10
"

ซึ่งหมายความว่าฉันเกรงว่าคุณจะต้องแสดงรูปแบบเป็นนิพจน์ทั่วไปที่เหมาะสม:

\n\nทดสอบ1\n\nทดสอบ2\n\n

อย่างไรก็ตาม นี่ก็หมายความว่าคุณต้องการ -ป ตั้งค่าสถานะเพื่อเปิดใช้งานนิพจน์ทั่วไปที่เข้ากันได้กับ Perl ดังนั้น \n จะทำงาน.

ฉันสร้างไฟล์ทั้งสองนี้เพื่อสาธิต:

$ ไฟล์แมว1
นี่คือการทดสอบ:
ทดสอบ1

ทดสอบ2
และอีกอันหนึ่ง

ไฟล์แมว $2
นี่คือการทดสอบ:

ทดสอบ1

ทดสอบ2

และอีกอันหนึ่ง

เมื่อใช้ไฟล์ทั้งสองนี้และข้อมูลด้านบน คุณสามารถทำได้:

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

$ grep -Pz '\n\ntest1\n\ntest2\n\n' ไฟล์2
นี่คือการทดสอบ:

ทดสอบ1

ทดสอบ2

และอีกอันหนึ่ง

การรวมทั้งหมดนี้เข้าด้วยกันทำให้เรา:

string='\n\ntest1\n\ntest2\n\n'
ถ้า ! grep -Pzq "$string" test.txt; แล้ว
    พิมพ์f "$string" >> test.txt
ไฟ

หรือตามที่แนะนำโดย @steeldriver ในความคิดเห็น คุณสามารถใช้ตัวแปรและแปลงการขึ้นบรรทัดใหม่เป็น \n ในขณะที่บิน:

$ สตริง = "

    ทดสอบ1

    ทดสอบ2

    "
$ ถ้า ! grep -Pzq "${string//$'\n'/\n}" test.txt; แล้ว
    พิมพ์f "$string" >> test.txt
ไฟ

หากสตริงของคุณมีอักขระพิเศษที่มีความหมายในนิพจน์ทั่วไป ดังที่คุณแสดงในคำถามที่อัปเดตแล้ว แสดงว่าเป็นสถานการณ์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิง สำหรับตัวอย่างที่คุณแสดง คุณต้องการบางสิ่งที่ซับซ้อนกว่านี้มาก แบบนี้:

searchString='\n\nif \[ -f ~/.script \]; จากนั้น\s*\n\s*\.\s+~/\.script\s*\nfi\n\n'
พิมพ์สตริง='
ถ้า [ -f ~/.script ]; แล้ว
   . ~/.script         
ไฟ

'
ถ้า ! grep -Pzq "$searchString" test.txt; แล้ว     
    printf "%s" "$printString" >> test.txt 
ไฟ
user56834 avatar
cn flag
ขอบคุณ! ฉันถือว่าคุณหมายถึง `ถ้า ! grep -q -z "$string" "test.txt"; แล้ว `, เช่น ด้วยการเพิ่ม -z?
user56834 avatar
cn flag
ที่จริง แม้จะเพิ่ม -z ปัญหาเดิมยังคงมีอยู่สำหรับฉัน ตามที่ฉันระบุไว้ในความคิดเห็นของคำถามเดิมของฉัน: นั่นคือ ด้วย `if ! grep -q -z "$string" "test.txt"; แล้ว ` หรือ ` ถ้า ! grep -q "$string" "test.txt"; แล้ว ` หรือ ` ถ้า ! grep -q -z "$string" test.txt; จากนั้น ` มันล้มเหลวในลักษณะที่ค่อนข้างแปลก:
terdon avatar
cn flag
@user56834 อ๊ะ ใช่ แต่สิ่งนี้จะไม่ทำงานกับตัวแปร ขอเวลาสักครู่ ฉันกำลังพยายามหาปัญหาอยู่
Terrance avatar
id flag
คูลิโอ! +1 การมีช่องว่างในสตริงเป็น `string='\n\n test1\n\n test2\n\n'` ก็ใช้ได้เช่นกัน :)
terdon avatar
cn flag
@ user56834 โปรดดูคำตอบที่อัปเดต
terdon avatar
cn flag
@steeldriver หึ! ขอบคุณ ฉันสามารถสาบานได้ว่าฉันทำ แต่ไม่ ฉันเพิ่งทดสอบในเทอร์มินัลแล้วลืม แก้ไขแล้ว ขอบคุณ
user56834 avatar
cn flag
ขออภัยในความล่าช้า และขอขอบคุณ! เกี่ยวกับคำแนะนำของ steeldriver ฉันได้รับข้อผิดพลาดอย่างประหลาด: "substtest.sh 12: Bad substitution"
user56834 avatar
cn flag
โอ้ ไม่เป็นไร ดูเหมือนว่าสิ่งนี้จะแก้ไขได้ด้วยการเรียกใช้งานสคริปต์ .sh ด้วย bash แทน sh (เส้นประ) ไม่แน่ใจว่าทำไม มันได้ผล! ยอดเยี่ยม. (แม้ว่าฉันจะไม่เข้าใจส่วน "//$'\n'/\n}" มีคำอธิบายที่ดีหรือไม่)
terdon avatar
cn flag
@ user56834 `dash` และ `sh` คือ _not_ `bash` และไม่ควรถือว่ามีความหมายเหมือนกัน Dash เป็นเชลล์ POSIX ขั้นต่ำและขาดคุณสมบัติหลายอย่างของเชลล์ 'bash' ที่ซับซ้อนกว่า กันไปสำหรับ `sh` สำหรับ `"${string//$'\n'/\n}"` นั่นคือการแทนที่ (เฉพาะ bash) รูปแบบทั่วไปคือ "${var//old/new}" ซึ่งจะแทนที่ "เก่า" ทั้งหมดด้วย "ใหม่" ในตัวแปร "$var"ที่นี่ "เก่า" คือ `$'\n'` ซึ่งเป็นวิธีการขึ้นบรรทัดใหม่ไปยังเชลล์
user56834 avatar
cn flag
อันที่จริง ฉันเพิ่งลองทำแบบเดียวกันนี้กับกรณีที่ซับซ้อนกว่า และสิ่งนี้ทำให้มันพัง ดูคำถามเดิมของฉัน
Score:4
ธง hr

คุณอาจต้องการพิจารณาใช้ พีซีเครป กับ -ม หรือ --หลายบรรทัด ตัวเลือกเพื่ออนุญาตให้จับคู่การขึ้นบรรทัดใหม่ตามตัวอักษร:

   -M, --หลายบรรทัด
             อนุญาตให้รูปแบบจับคู่มากกว่าหนึ่งบรรทัด เมื่อตัวเลือกนี้
             ได้รับ รูปแบบอาจมีตัวอักษรขึ้นบรรทัดใหม่ charâ charâ
             นักแสดงและเหตุการณ์ภายในของอักขระ ^ และ $

อดีต. ที่ให้ไว้

$ cat test.txt
นี่คือการทดสอบ:
ทดสอบ1

ทดสอบ2
และอีกอันหนึ่ง


    ทดสอบ1

    ทดสอบ2
    
    

และ

$ cat test2.txt
นี่คือการทดสอบ:
ทดสอบ1

ทดสอบ2
และอีกอันหนึ่ง


    ทดสอบ 3

    ทดสอบ4
    
    

กับ

$ สตริง = "

    ทดสอบ1

    ทดสอบ2

    "

แล้ว

$ pcregrep -qM "$string" test.txt && echo 'found' || 'ไม่พบ'
พบ

$ pcregrep -qM "$string" test2.txt && echo 'found' || 'ไม่พบ'
ไม่พบ
user56834 avatar
cn flag
ขอบคุณ มันได้ผล น่าเสียดายที่มันล้มเหลวสำหรับตัวอย่างที่เหมือนจริงมากขึ้น ซึ่งฉันได้เพิ่มเข้าไปในคำถามของฉัน (แค่คำตอบของ sterdon ที่ล้มเหลวในตัวอย่างนั้น)
hr flag
@ user56834 นั่นอาจเป็นเพราะ `[ ... ]` แสดงถึงช่วงอักขระใน PCRE ลองแทนที่ `"$string"` ด้วย `"\Q${string}\E"`
user56834 avatar
cn flag
ตอบกลับช้ากว่าเล็กน้อย แต่: คุณช่วยชี้ฉันไปยังสถานที่ที่ฉันสามารถอ่านเกี่ยวกับสิ่งที่ \Q และ \E ทำอย่างไร
hr flag
@ user56834 ลอง [quotemeta] ของ perldoc (https://perldoc.perl.org/functions/quotemeta)
Score:2
ธง cn

การค้นหารูปแบบหลายบรรทัดในไฟล์อาจง่ายกว่าด้วย awk:

awk '/เริ่มรูปแบบ/,/สิ้นสุดรูปแบบ/' ชื่อไฟล์

ตรวจสอบ โพสต์นี้ สำหรับรายละเอียดเพิ่มเติม

โพสต์คำตอบ

คนส่วนใหญ่ไม่เข้าใจว่าการถามคำถามมากมายจะปลดล็อกการเรียนรู้และปรับปรุงความสัมพันธ์ระหว่างบุคคล ตัวอย่างเช่น ในการศึกษาของ Alison แม้ว่าผู้คนจะจำได้อย่างแม่นยำว่ามีคำถามกี่ข้อที่ถูกถามในการสนทนา แต่พวกเขาไม่เข้าใจความเชื่อมโยงระหว่างคำถามและความชอบ จากการศึกษาทั้ง 4 เรื่องที่ผู้เข้าร่วมมีส่วนร่วมในการสนทนาด้วยตนเองหรืออ่านบันทึกการสนทนาของผู้อื่น ผู้คนมักไม่ตระหนักว่าการถามคำถามจะมีอิทธิพลหรือมีอิทธิพลต่อระดับมิตรภาพระหว่างผู้สนทนา