Grep: ผลลัพธ์ที่ไม่คาดคิดเมื่อค้นหาคำในหัวข้อจากหน้าคน


19

ฉันพบพฤติกรรมแปลก ๆ เมื่อพยายาม grep man page ใน macOS ตัวอย่างเช่นหน้า Bash man มีการเกิดขึ้นของสตริงอย่างชัดเจนNAME:

$ man bash | head -5 | tail -1
NAME

และถ้าฉัน grep สำหรับnameฉันจะได้รับผลลัพธ์ แต่ถ้าฉัน grep สำหรับNAMEฉันไม่ได้:

$ man bash | grep 'NAME'
$ man bash | grep NAME

ฉันลองคำอื่น ๆ ที่ฉันรู้ว่ามีอยู่แล้วและค้นหาสิ่งที่ให้SHELLผลตอบแทนในขณะที่ค้นหาBASHผลลัพธ์

เกิดอะไรขึ้นที่นี่?

ปรับปรุง : ขอบคุณสำหรับคำตอบทั้งหมด! ฉันคิดว่ามันคุ้มค่าที่จะเพิ่มบริบทที่ฉันพบเจอ ฉันต้องการเขียนฟังก์ชัน bash เพื่อตัดคำmanและในกรณีที่ฉันพยายามค้นหา man page สำหรับ shell builtin ให้ข้ามไปยังส่วนที่เกี่ยวข้องของ Bash man page อาจมีวิธีที่ดีกว่านี้ แต่นี่คือสิ่งที่ฉันมีในขณะนี้:

man () {
  case "$(type -t "$1")" in
    builtin)
      local pattern="^ *$1"

      if bashdoc_match "$pattern \+[-[]"; then
        command man bash | less --pattern="$pattern +[-[]"
      elif bashdoc_match "$pattern\b"; then
        command man bash | less --pattern="$pattern[[:>:]]"
      else
        command man bash
      fi
      ;;
    keyword)
      command man bash | less --hilite-search --pattern='^SHELL GRAMMAR$'
      ;;
    *)
      command man "$@"
      ;;
  esac
}

bashdoc_match() {
  command man bash | col -b | grep -l "$1" > /dev/null
}


คุณใช้ระบบปฏิบัติการอะไร ฉันแน่ใจว่าคำตอบที่ยอมรับนั้นถูกต้อง แต่ IO ไม่สามารถทำซ้ำได้ในช่อง Arch Linux ของฉัน man bash | grep NAMEทำงานตามที่คาดไว้
terdon

@terdon ฉันใช้ macOS ฉันได้รับพฤติกรรมนี้ด้วย Bash 3.2 และ 4.4.5
ivan

เช่นกัน: หากคุณตรวจพบบิวด์อินคุณสามารถใช้helpคำสั่งbash เพื่อรับข้อมูล
Joe

@ โจปัญหาคือฉันมักจะพบว่าhelpผลออกมามากเกินไป ตรวจสอบhelp completevs completeส่วนในman bashตัวอย่างเช่น
ivan

คำตอบ:


33

ถ้าคุณเพิ่ม| sed -n lว่าtailคำสั่งที่จะแสดงตัวอักษรที่ไม่สามารถพิมพ์คุณอาจจะเห็นสิ่งที่ชอบ:

N\bNA\bAM\bME\bE

นั่นคือตัวละครแต่ละตัวเขียนเป็นBackspaceX Xบนเทอร์มินัลสมัยใหม่ตัวละครจบลงด้วยการเขียนทับตัวเอง (เช่น Backspace หรือ BS aka \baka ^Hคือตัวละครที่ย้ายเคอร์เซอร์หนึ่งคอลัมน์ไปทางซ้าย) โดยไม่มีความแตกต่าง แต่ในเครื่องพิมพ์ดีดโบราณซึ่งจะทำให้ตัวละครปรากฏเป็นตัวหนาเมื่อได้รับหมึกมากเป็นสองเท่า

แต่ถึงกระนั้นวิทยุติดตามตัวเช่นmore/ lessเข้าใจรูปแบบนั้นหมายถึงตัวหนาดังนั้นนั่นคือสิ่งที่roffจะส่งออกข้อความตัวหนา

การใช้งานของผู้ชายบางคนจะเรียกroffในลักษณะที่ไม่ใช้ลำดับเหล่านั้น (หรือเรียกภายในcol -b -p -xเพื่อดึงพวกเขาเช่นในกรณีของการman-dbใช้งาน (เว้นแต่MAN_KEEP_FORMATTINGตัวแปรสภาพแวดล้อมที่มีการตั้งค่า)) และไม่เรียกเพจเจอร์เมื่อพวกเขาตรวจพบผลลัพธ์ จะไม่ไปที่เทอร์มินัล ( man bash | grep NAMEจะทำงานที่นั่น) แต่ไม่ใช่ของคุณ

คุณสามารถใช้col -bเพื่อลบลำดับเหล่านั้น (มีประเภทอื่น ๆ ( _BS X) เช่นเดียวกับขีดเส้นใต้)

สำหรับระบบที่ใช้ GNU roff(เช่น GNU หรือ FreeBSD) คุณสามารถหลีกเลี่ยงลำดับผู้ที่ถูกนำมาใช้ในครั้งแรกโดยการทำให้แน่ใจว่า-c -b -uตัวเลือกที่จะส่งผ่านไปgrottyตัวอย่างเช่นโดยการทำให้แน่ใจว่าตัวเลือกที่ถูกส่งไปยัง-P-cbugroff

ตัวอย่างเช่นโดยการสร้างสคริปต์ wrapper ที่เรียกว่าgroffมี:

#! /bin/sh -
exec /usr/bin/groff -P-cbu "$@"

ที่คุณใส่ไปข้างหน้าของ / usr / bin / Groff $PATHใน

ด้วย macOS ' man(เช่นใช้ GNU roff) คุณสามารถสร้าง a man-no-overstrike.confด้วย:

NROFF /usr/bin/groff -mandoc -Tutf8 -P-cbu

และโทรmanเป็น:

man -C man-no-overstrike.conf bash | grep NAME

ยังคงอยู่กับ GNU roffถ้าคุณตั้งค่าGROFF_SGRตัวแปรสภาพแวดล้อม (หรือไม่ตั้งค่าGROFF_NO_SGRตัวแปรขึ้นอยู่กับวิธีการตั้งค่าเริ่มต้นในเวลารวบรวม) จากนั้นgrotty(ตราบใดที่ยังไม่ผ่าน-cตัวเลือก) จะใช้ลำดับการหลบหนีเทอร์มินัล ANSI SGR แทน ของเทคนิค BS เหล่านั้นสำหรับคุณลักษณะของตัวละคร lessเข้าใจพวกเขาเมื่อถูกเรียกพร้อมกับ-Rตัวเลือก

คน FreeBSD โทรหาgrottyด้วย-cตัวเลือกเว้นแต่คุณจะถามหาสีโดยการตั้งค่าตัวแปร MANCOLOR (ในกรณีที่-cไม่ผ่านgrottyและgrottyกลับไปใช้ค่าเริ่มต้นของการใช้ลำดับการหลีกหนีของ ANSI SGR ที่นั่น)

MANCOLOR=1 man bash | grep NAME

จะทำงานที่นั่น

บน Debian นั้น GROFF_SGR ไม่ใช่ค่าเริ่มต้น ถ้าคุณทำ:

GROFF_SGR=1 man bash | grep NAME

อย่างไรก็ตามเนื่องจากmanstdout ไม่ใช่เทอร์มินัลจึงต้องผ่านตัวเองเพื่อส่งGROFF_NO_SGRตัวแปรไปยังgrotty(ฉันคิดว่ามันสามารถใช้col -bpxเพื่อตัดลำดับ BS ได้เนื่องจากcolไม่ทราบว่าจะตัดลำดับ SGR ได้อย่างไรแม้ว่ามันจะยังคง ไม่ได้ด้วยMAN_KEEP_FORMATTING) GROFF_SGRซึ่งแทนที่เรา คุณสามารถทำได้:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(ในเทอร์มินัล) เพื่อให้ลำดับหนี SGR

เวลานั้นคุณจะสังเกตเห็นว่าNAMEบางรายการปรากฏเป็นตัวหนาในหน้าจอเทอร์มินัล (และในless -Rเพจเจอร์) หากคุณป้อนผลลัพธ์ไปที่sed -n l( MANPAGER='sed -n /NAME/l') คุณจะเห็นสิ่งต่อไปนี้:

\033[1mNAME\033[0m$

ที่ไหน\e[1mเป็นลำดับเพื่อเปิดใช้งานตัวหนาในอาคารที่รองรับมาตรฐาน ANSI และ\e[0mลำดับเพื่อกลับแอตทริบิวต์ SGR ทุกคนที่จะเริ่มต้น

ในข้อความนั้นgrep NAMEทำงานเหมือนข้อความนั้นNAMEแต่คุณยังคงมีปัญหาหากค้นหาข้อความที่มีเพียงบางส่วนของตัวหนา / ขีดเส้นใต้ ...


2
ว้าวน่าสนใจทีเดียวที่ได้เห็นมรดกทางกายภาพที่นั่น หมึกมากเป็นสองเท่า => ตัวหนา ทำให้รู้สึกที่สมบูรณ์แบบ
ivan

1
ฉันรักเป็นตัวแทนสำหรับsed -n l od
Tom Hale

13

หากคุณดูหน้าคู่มือใด ๆ คุณจะสังเกตเห็นว่าส่วนหัวเป็นตัวหนา สิ่งนี้สามารถทำได้โดยการจัดรูปแบบด้วยอักขระควบคุม เพื่อให้สามารถgrepเหมือนที่คุณต้องการสิ่งเหล่านี้จะต้องถูกถอดออก

colยูทิลิตี้อาจจะใช้สำหรับนี้:

$ man bash | col -b | grep 'NAME'

-bตัวเลือกที่มีรายละเอียดต่อไปใน OpenBSD :

ห้ามส่ง backspaces ใด ๆ โดยพิมพ์เฉพาะอักขระตัวสุดท้ายที่เขียนไปยังตำแหน่งคอลัมน์แต่ละตำแหน่ง สิ่งนี้มีประโยชน์ในการประมวลผลเอาต์พุตของ mandoc (1)


ลินุกซ์ colคู่มือ (บน Ubuntu) ไม่มีประโยคสุดท้ายในนั้น (แต่ใช้งานได้ในวิธีเดียวกัน)

บน Linux unsetting MAN_KEEP_FORMATTINGตัวแปรสภาพแวดล้อม (หรือการตั้งค่าให้เป็นสตริงว่าง) อาจยังความช่วยเหลือและจะช่วยให้คุณได้grepโดยไม่ต้องผ่านการส่งออกของผ่านmancol -b


ฉันคิดว่า (อย่างที่ฉันทดสอบบน Arch และระบบ Ubuntu) ว่าบน Linux นี่ไม่จำเป็นหรือไม่อีกต่อไป ในระบบทั้งสองNAMEในคู่มือการทุบตีเป็นเพียงไม่มีNAME \b
terdon

@terdon ฉันไม่ได้เห็นการกล่าวถึง macOS เป็นอันดับแรกดังนั้นฉันจึงคิดว่าระบบ Linux ที่ตั้งค่าผิดพลาดเป็นไปได้ ตอนนี้ฉันได้ตัดส่วนบิตของ Linux ออก
Kusalananda

คุณไม่พลาดอะไรเลยฉันถาม OP ว่าพวกเขาใช้ระบบปฏิบัติการแบบใดเพราะฉันไม่สามารถทำซ้ำได้บน Linux พวกเขากล่าวว่า macOS และฉันเพิ่งเพิ่มเข้าไปในตอนนี้ และฉันก็ไม่ได้หมายความว่าคุณผิดเพราะทุกสิ่งที่ฉันรู้ก็คือมีดิสทริบิวชั่นลีนุกซ์อยู่ตรงนั้นซึ่งMAN_KEEP_FORMATTINGตัวแปรทำงานได้ดีอย่างที่คุณบอก ฉันแค่อยากจะชี้ให้เห็นว่านั่นไม่ใช่กรณีเสมอไป
terdon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.