ฉันเรียนรู้ Linux และฉันมีความท้าทายที่ฉันไม่สามารถแก้ไขได้ด้วยตัวเอง นี่มันคือ:
grep บรรทัดจากไฟล์ที่มีตัวเลข 4 ตัวในแถว แต่ไม่เกิน 4
ฉันไม่แน่ใจว่าจะเข้าถึงสิ่งนี้ได้อย่างไร ฉันสามารถค้นหาตัวเลขเฉพาะ แต่ไม่ได้จำนวนในสตริง
\b\d{4}\b
ฉันเรียนรู้ Linux และฉันมีความท้าทายที่ฉันไม่สามารถแก้ไขได้ด้วยตัวเอง นี่มันคือ:
grep บรรทัดจากไฟล์ที่มีตัวเลข 4 ตัวในแถว แต่ไม่เกิน 4
ฉันไม่แน่ใจว่าจะเข้าถึงสิ่งนี้ได้อย่างไร ฉันสามารถค้นหาตัวเลขเฉพาะ แต่ไม่ได้จำนวนในสตริง
\b\d{4}\b
คำตอบ:
มีสองวิธีในการตีความคำถามนี้ ฉันจะอยู่ทั้งสองกรณี คุณอาจต้องการแสดงบรรทัด:
ตัวอย่างเช่น (1) จะแสดง1234a56789แต่ (2) จะไม่
หากคุณต้องการแสดงทุกบรรทัดที่มีลำดับของตัวเลขสี่หลักที่ไม่ได้เป็นส่วนหนึ่งของลำดับของตัวเลขอีกต่อไปทางเดียวคือ:
grep -P '(?<!\d)\d{4}(?!\d)' file
การใช้งานนี้นิพจน์ปกติ Perlซึ่งของ Ubuntu grep( GNU grep ) -Pสนับสนุนผ่านทาง มันจะไม่ตรงกับข้อความเช่น12345และจะไม่ตรงกับ1234หรือ2345เป็นส่วนหนึ่งของมัน แต่มันจะตรงกับใน12341234a56789
ในการแสดงออกปกติ Perl:
\dหมายถึงตัวเลขใด ๆ (มันเป็นวิธีสั้น ๆ ในการพูด[0-9]หรือ[[:digit:]])x{4}ตรงกับx4 ครั้ง ( { }ไวยากรณ์ไม่ได้เฉพาะการแสดงออกปกติ Perl; มันอยู่ในการแสดงออกปกติขยายผ่านgrep -Eเช่นกัน.) ดังนั้นเป็นเช่นเดียวกับ\d{4}\d\d\d\d(?<!\d)เป็นการยืนยันเชิงลบที่ดูเป็นศูนย์ มันหมายถึง "เว้นแต่นำหน้าด้วย\d"(?!\d)เป็นการยืนยันล่วงหน้าที่เป็นค่าลบในการมองไปข้างหน้า มันหมายถึง "เว้นแต่ตามด้วย\d"(?<!\d)และ(?!\d)ไม่ตรงกับข้อความนอกลำดับสี่หลัก แต่พวกเขาจะ (เมื่อใช้ร่วมกัน) ป้องกันไม่ให้ลำดับสี่หลักจากตัวเองถูกจับคู่ถ้ามันเป็นส่วนหนึ่งของลำดับของตัวเลขอีกต่อไป
การใช้เพียงการมองด้านหลังหรือการมองไปข้างหน้านั้นไม่เพียงพอเนื่องจากการเรียงลำดับตัวเลขสี่หลักทางขวาสุดหรือซ้ายสุดจะยังคงจับคู่อยู่
ประโยชน์อย่างหนึ่งของการใช้การมองด้านหลังและการยืนยันล่วงหน้าคือรูปแบบของคุณจะจับคู่กับตัวเลขสี่หลักเท่านั้นไม่ใช่ข้อความที่อยู่รอบ ๆ สิ่งนี้มีประโยชน์เมื่อใช้การเน้นสี (พร้อม--colorตัวเลือก)
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
โดยค่าเริ่มต้นในอูบุนตูผู้ใช้แต่ละคนมีalias grep='grep --color=auto'อยู่ในพวกเขาไฟล์~.bashrc ดังนั้นคุณจะได้รับการเน้นสีโดยอัตโนมัติเมื่อคุณเรียกใช้คำสั่งง่าย ๆ เริ่มต้นด้วยgrep(นี่คือเมื่อมีการขยายนามแฝง ) และเอาท์พุทมาตรฐานคือขั้ว (นี่คือสิ่งที่ตรวจสอบ) โดยทั่วไปแล้วการจับคู่จะถูกไฮไลต์ในเฉดสีแดง (ใกล้กับแดง ) แต่ฉันได้แสดงให้เห็นว่าเป็นตัวหนาตัวเอียง นี่คือภาพหน้าจอ:--color=auto

และคุณสามารถgrepพิมพ์เฉพาะข้อความที่ตรงกันเท่านั้นและไม่ใช่ทั้งบรรทัดด้วย-o:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
อย่างไรก็ตามหากคุณ:
grepไม่รองรับ-Pหรือไม่ต้องการใช้นิพจน์ปกติของ Perl และ... จากนั้นคุณสามารถบรรลุสิ่งนี้ด้วยนิพจน์ทั่วไปที่ขยายเพิ่มแทน:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
ซึ่งตรงกับตัวเลขสี่หลักและอักขระที่ไม่ใช่ตัวเลข - หรือจุดเริ่มต้นหรือจุดสิ้นสุดของบรรทัด - ล้อมรอบพวกเขา โดยเฉพาะ:
[0-9]จับคู่ตัวเลขใด ๆ (เช่น[[:digit:]]หรือ\dในการแสดงออกปกติ Perl) และ{4}หมายถึง "สี่ครั้ง" เพื่อให้[0-9]{4}ตรงกับลำดับสี่หลัก[^0-9]ตรงกับตัวอักษรที่ไม่ได้อยู่ในช่วงของผ่าน0 9มันเทียบเท่ากับ[^[:digit:]](หรือ\Dในการแสดงออกปกติ Perl)^เมื่อไม่ปรากฏใน[ ]เครื่องหมายวงเล็บให้จับคู่ส่วนต้นของบรรทัด ในทำนองเดียวกัน$ตรงกับจุดสิ้นสุดของบรรทัด|หมายถึงหรือและวงเล็บคือสำหรับการจัดกลุ่ม (เช่นในพีชคณิต) ดังนั้น(^|[^0-9])ตรงกับจุดเริ่มต้นของบรรทัดหรืออักขระที่ไม่ใช่ตัวเลขในขณะที่($|[^0-9])ตรงกับจุดสิ้นสุดของบรรทัดหรืออักขระที่ไม่ใช่ตัวเลขดังนั้นการจับคู่เกิดขึ้นเฉพาะในบรรทัดที่มีลำดับตัวเลขสี่หลัก ( [0-9]{4}) ที่พร้อมกัน:
(^|[^0-9])) และ($|[^0-9]))ในทางกลับกันหากคุณต้องการแสดงทุกบรรทัดที่มีลำดับสี่หลัก แต่ไม่มีลำดับมากกว่าสี่หลักใด ๆ (แม้แต่อันที่แยกจากอีกสี่หลักเท่านั้น) จากนั้นให้แนวคิดของคุณ เป้าหมายคือการค้นหาบรรทัดที่ตรงกับรูปแบบหนึ่ง แต่ไม่ใช่แบบอื่น
ดังนั้นแม้ว่าคุณจะรู้วิธีการทำด้วยรูปแบบเดียวฉันขอแนะนำให้ใช้บางอย่างเช่นข้อเสนอแนะที่สองของแมตต์โดยgrepแยกเป็นสองรูปแบบ
คุณไม่ได้รับประโยชน์อย่างมากจากฟีเจอร์ขั้นสูงใด ๆ ของการแสดงออกปกติของ Perl เมื่อทำเช่นนั้นดังนั้นคุณอาจไม่ต้องการใช้มัน แต่เพื่อให้สอดคล้องกับสไตล์ข้างต้นนี่เป็นวิธีการแก้ปัญหาแบบสั้นโดยใช้\d(และวงเล็บปีกกา) แทน[0-9]:
grep -P '\d{4}' file | grep -Pv '\d{5}'
เพราะมันใช้[0-9], วิธีการของแมตต์เป็นแบบพกพาอื่น ๆ - มันจะทำงานบนระบบที่grepไม่สนับสนุนการแสดงออกปกติ Perl หากคุณใช้[0-9](หรือ[[:digit:]]) แทน\dแต่ยังคงใช้งานต่อไป{ }คุณจะได้รับความสะดวกในการพกพามากขึ้น:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
ถ้าคุณชอบgrepคำสั่งจริงๆ
greps คั่นด้วยไพพ์ดังที่แสดงด้านบน)... จากนั้นคุณสามารถใช้:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
การ-xตั้งค่าสถานะทำให้grepแสดงเฉพาะบรรทัดที่ทั้งบรรทัดตรงกัน (แทนที่จะเป็นบรรทัดใด ๆที่มีการจับคู่)
ฉันใช้นิพจน์ปกติของ Perl เพราะฉันคิดว่าช่วงเวลาสั้น ๆ\dและ\Dเพิ่มความชัดเจนในกรณีนี้ แต่ถ้าคุณต้องการบางสิ่งบางอย่างแบบพกพาไปยังระบบที่grepไม่สนับสนุน-Pคุณสามารถแทนที่ด้วย[0-9]และ[^0-9](หรือด้วย[[:digit:]]และ[^[:digit]]):
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
วิธีการทำงานของนิพจน์ทั่วไปเหล่านี้คือ:
ตรงกลาง\d{4}หรือ[0-9]{4}ตรงกับหนึ่งในสี่หลัก เราอาจมีมากกว่าหนึ่งสิ่งเหล่านี้ แต่เราต้องมีอย่างน้อยหนึ่งอย่าง
ทางด้านซ้าย(\d{0,4}\D)*หรือ([0-9]{0,4}[^0-9])*ตรงกับ*อินสแตนซ์ศูนย์หรือมากกว่า ( ) ที่มีตัวเลขไม่เกินสี่หลักตามด้วยตัวเลขที่ไม่ใช่ตัวเลข ตัวเลขศูนย์ (กล่าวคือไม่มีอะไร) คือความเป็นไปได้อย่างหนึ่งสำหรับ "ไม่เกินสี่หลัก" การจับคู่นี้(a)สตริงว่างหรือ(b)สตริงใด ๆ ที่ลงท้ายด้วยไม่ใช่ตัวเลขและไม่มีลำดับใด ๆ ที่มีตัวเลขมากกว่าสี่หลัก
เนื่องจากข้อความทางด้านซ้ายของกึ่งกลาง\d{4}(หรือ[0-9]{4}) ต้องว่างเปล่าหรือลงท้ายด้วยไม่ใช่ตัวเลขสิ่งนี้จะป้องกันไม่ให้ศูนย์กลาง\d{4}จับคู่สี่หลักที่มีอีก (ห้า) หลักทางด้านซ้ายของพวกเขา
ทางด้านขวา(\D\d{0,4})*หรือ([^0-9][0-9]{0,4})*ตรงกับ*อินสแตนซ์ที่เป็นศูนย์หรือมากกว่า ( ) ของไม่ใช่ตัวเลขตามด้วยตัวเลขไม่เกินสี่หลัก (ซึ่งเหมือนก่อนหน้านี้อาจเป็นสี่, สาม, สอง, หนึ่งหรือแม้กระทั่งไม่มีเลย) สิ่งนี้ตรงกับ(a)สตริงว่างหรือ(b)สตริงใด ๆ ที่เริ่มต้นด้วยตัวเลขที่ไม่ใช่ตัวเลขและไม่มีลำดับใด ๆ ที่มีตัวเลขมากกว่าสี่หลัก
เนื่องจากข้อความทางด้านขวาของศูนย์กลาง\d{4}(หรือ[0-9]{4}) จะต้องว่างเปล่าหรือเริ่มต้นด้วยไม่ใช่ตัวเลขสิ่งนี้จะป้องกันไม่ให้ศูนย์กลาง\d{4}จับคู่สี่หลักที่มีอีก (ห้า) หลักทางด้านขวาของพวกเขา
สิ่งนี้ช่วยให้มั่นใจว่ามีลำดับสี่หลักอยู่ที่ใดที่หนึ่งและไม่มีลำดับของตัวเลขห้าหลักขึ้นไป
มันไม่เลวหรือผิดที่จะทำเช่นนี้ แต่บางทีเหตุผลที่สำคัญที่สุดในการพิจารณาทางเลือกนี้ก็คือมันช่วยให้ชัดเจนประโยชน์ของการใช้(หรือคล้ายกัน) แทนตามที่แนะนำข้างต้นและคำตอบของแมตต์grep -P '\d{4}' file | grep -Pv '\d{5}'
ด้วยวิธีดังกล่าวมันชัดเจนว่าเป้าหมายของคุณคือการเลือกบรรทัดที่มีสิ่งใดสิ่งหนึ่ง แต่ไม่ใช่สิ่งอื่น นอกจากนี้ไวยากรณ์ยังง่ายขึ้น (ดังนั้นผู้อ่าน / ผู้ดูแลรักษาหลายคนอาจเข้าใจได้เร็วขึ้น)
สิ่งนี้จะแสดงตัวเลข 4 ตัวติดต่อกัน แต่ไม่เกิน
grep '[0-9][0-9][0-9][0-9][^0-9]' file
หมายเหตุ ^ หมายถึงไม่
มีปัญหากับสิ่งนี้แม้ว่าฉันจะไม่แน่ใจว่าจะแก้ไขได้อย่างไร ... ถ้าจำนวนท้ายบรรทัดแล้วมันจะไม่ปรากฏขึ้น
รุ่น uglier นี้ แต่จะทำงานสำหรับกรณีที่
grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]
a12345b 2345b
หากgrepไม่รองรับนิพจน์ทั่วไปของ Perl ( -P) ให้ใช้คำสั่ง shell ต่อไปนี้:
grep -w "$(printf '[0-9]%.0s' {1..4})" file
ที่printf '[0-9]%.0s' {1..4}จะผลิต 4 [0-9]ครั้ง วิธีนี้มีประโยชน์เมื่อคุณมีตัวเลขยาวและคุณไม่ต้องการที่จะทำซ้ำรูปแบบ (เพียงแทนที่4ด้วยตัวเลขของคุณตัวเลขเพื่อค้นหา)
การใช้-wจะมองหาคำทั้งหมด อย่างไรก็ตามหากคุณสนใจสตริงตัวอักษรและตัวเลขเช่น1234aจากนั้นเพิ่ม[^0-9]ที่ส่วนท้ายของรูปแบบเช่น
grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file
ใช้$()เป็นพื้นแทนคำสั่ง ตรวจสอบโพสต์นี้เพื่อดูวิธีการprintfซ้ำรูปแบบ
คุณสามารถลองคำสั่งด้านล่างโดยแทนที่fileด้วยชื่อไฟล์จริงในระบบของคุณ:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
คุณสามารถตรวจสอบบทช่วยสอนนี้สำหรับการใช้คำสั่ง grep เพิ่มเติม
1234a12345กล่าวหรือไม่?