ฉันเรียนรู้ 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
เป็นส่วนหนึ่งของมัน แต่มันจะตรงกับใน1234
1234a56789
ในการแสดงออกปกติ Perl:
\d
หมายถึงตัวเลขใด ๆ (มันเป็นวิธีสั้น ๆ ในการพูด[0-9]
หรือ[[:digit:]]
)x{4}
ตรงกับx
4 ครั้ง ( {
}
ไวยากรณ์ไม่ได้เฉพาะการแสดงออกปกติ 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
คำสั่งจริงๆ
grep
s คั่นด้วยไพพ์ดังที่แสดงด้านบน)... จากนั้นคุณสามารถใช้:
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
กล่าวหรือไม่?