Grep: nieoczekiwane wyniki podczas wyszukiwania słów w nagłówku ze strony podręcznika man


19

Występują dziwne zachowania, gdy próbuję grepować stronę podręcznika systemowego w systemie macOS. Na przykład strona podręcznika Bash ma wyraźnie wystąpienie ciągu NAME:

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

A jeśli grepuję za name, to dostaję wyniki, ale jeśli grepuję za NAME, nie:

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

Próbowałem innych wielkich liter, o których wiem, że tam są, i szukanie wyników SHELLnic nie daje, podczas gdy wyszukiwanie BASHwyników daje wyniki.

Co tu się dzieje?

Aktualizacja : Dziękujemy za wszystkie odpowiedzi! Pomyślałem, że warto dodać kontekst, w którym na to wpadłem. Chciałem napisać funkcję bash do zawinięcia, mana w przypadkach, gdy próbowałem wyszukać stronę podręcznika dla wbudowanej powłoki, przeskocz do odpowiedniej sekcji strony podręcznika Bash. Może być lepszy sposób, ale oto co mam obecnie:

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
}


Jakiego systemu operacyjnego używasz? Jestem pewien, że zaakceptowana odpowiedź jest poprawna, ale IO nie może jej odtworzyć na moim komputerze Arch Linux. man bash | grep NAMEdziała zgodnie z oczekiwaniami.
terdon

@terdon Jestem na macOS. Takie zachowanie dostaję w Bash 3.2 i 4.4.5
ivan

Na marginesie: jeśli wykryjesz wbudowane narzędzie, możesz po prostu użyć helppolecenia bash, aby uzyskać jego informacje.
Joe

@ Joe Problem polega na tym, że często stwierdzam, że helpwyniki zbyt wiele pomijają. Sprawdź na help completeprzykład completesekcję w man bash.
ivan

Odpowiedzi:


33

Jeśli dodasz | sed -n ldo tego tailpolecenia, aby wyświetlać znaki niedrukowalne, prawdopodobnie zobaczysz coś takiego:

N\bNA\bAM\bME\bE

Oznacza to, że każdy znak jest pisany jako XBackspace X. Na nowoczesnych terminalach znak kończy się nad sobą (ponieważ Backspace aka BS aka \baka ^Hto znak, który przesuwa kursor o jedną kolumnę w lewo) bez różnicy. Ale w starożytnych maszynach do pisania to sprawiałoby, że postać pojawiała się pogrubioną czcionką, ponieważ otrzymywała dwa razy więcej atramentu.

Mimo to, pagery takie jak more/ lessdo rozumieją ten format jako pogrubiony, więc nadal to roffrobi, aby uzyskać pogrubiony tekst.

Niektóre implementacje man wywoływałyby roffw taki sposób, że te sekwencje nie byłyby używane (lub byłyby wywoływane wewnętrznie, col -b -p -xaby je rozebrać, tak jak w przypadku man-dbimplementacji (chyba że MAN_KEEP_FORMATTINGustawiono zmienną środowiskową)) i nie wywoływały pagera po wykryciu wyniku nie jedzie do terminalu (więc man bash | grep NAMEby tam działał), ale nie twój.

Możesz użyć col -bdo usunięcia tych sekwencji (istnieją również inne typy ( _BS X) również dla podkreślenia).

W przypadku systemów korzystających z GNU roff(takich jak GNU lub FreeBSD) możesz uniknąć używania tych sekwencji, upewniając się, że -c -b -uopcje zostały przekazane grotty, na przykład upewniając się, że -P-cbuopcje zostały przekazane groff.

Na przykład poprzez utworzenie skryptu opakowania o nazwie groffzawierającego:

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

Które umieściłeś przed / usr / bin / groff $PATH.

W systemie macOS ' man(również używającym GNU roff) możesz utworzyć man-no-overstrike.conf:

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

I zadzwoń manjako:

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

Nadal w GNU roff, jeśli ustawisz GROFF_SGRzmienną środowiskową (lub nie ustawisz GROFF_NO_SGRzmiennej w zależności od tego, jak domyślne zostały ustawione w czasie kompilacji), wtedy grotty(o ile nie zostanie przekazana -copcja) użyje zamiast tego sekwencji ucieczki terminala ANSI SGR tych sztuczek BS dla atrybutów postaci. lesszrozumieć je, gdy zostanie wywołany z -Ropcją.

Man wywołuje FreeBSD grottyz tą -copcją, chyba że pytasz o kolory , ustawiając zmienną MANCOLOR (w którym -cto przypadku nie jest przekazywany grottyi grottypowraca do domyślnego użycia sekwencji specjalnych ANSI SGR).

MANCOLOR=1 man bash | grep NAME

będzie tam działać.

W Debianie GROFF_SGR nie jest domyślny. Jeśli zrobisz:

GROFF_SGR=1 man bash | grep NAME

jednak ponieważ standardowe manwyjście nie jest terminalem, na siebie spoczywa również obowiązek przekazania GROFF_NO_SGRzmiennej do grotty(przypuszczam, że może on użyć col -bpxdo rozebrania sekwencji BS, ponieważ colnie wie, jak rozebrać sekwencje SGR, mimo że nadal robi to z MAN_KEEP_FORMATTING), co zastępuje nasze GROFF_SGR. Możesz zamiast tego:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(w terminalu), aby mieć sekwencje specjalne SGR.

Tym razem zauważysz, że niektóre z tych NAZW są pogrubione na terminalu (i na less -Rpagerze). Jeśli podasz wynik do sed -n l( MANPAGER='sed -n /NAME/l'), zobaczysz coś takiego:

\033[1mNAME\033[0m$

Gdzie \e[1mjest sekwencja, aby włączyć pogrubienie w terminalach kompatybilnych z ANSI, a \e[0msekwencja, aby przywrócić wszystkie atrybuty SGR do wartości domyślnej.

Ten tekst grep NAMEdziała tak, jak on zawiera tekst NAME, ale nadal możesz mieć problemy, jeśli szukasz tekstu, w którym tylko jego części są pogrubione / podkreślone ...


2
Wow, całkiem interesujące zobaczyć tam spuściznę fizycznego tele-typu. Dwa razy więcej atramentu => pogrubienie. Ma sens
ivan

1
Kocham sed -n ljako substytut od.
Tom Hale,

13

Jeśli spojrzysz na jakąkolwiek stronę podręcznika, zauważysz, że nagłówki są pogrubione. Osiąga się to poprzez sformatowanie ich za pomocą znaków kontrolnych. Aby móc greppolubić to, co chcesz, należy je rozebrać.

colNarzędzie może być wykorzystywane do tego:

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

-bOpcja ma następujący opis w OpenBSD :

Nie wypisuj żadnych spacji, wypisuje tylko ostatni znak zapisany w każdej pozycji kolumny. Może to być przydatne w przetwarzaniu danych wyjściowych mandoc (1).


Linux colinstrukcja (na Ubuntu) nie zawiera ostatniego zdania (ale działa w ten sam sposób).

W systemie Linux, wyłączające MAN_KEEP_FORMATTINGzmienną środowiskową (lub ustawienie go na pusty ciąg znaków) może również pomóc, a pozwoli Ci grepbez przechodzenia wyjście manpoprzez col -b.


Wydaje mi się (jak w tym przypadku przetestowałem to na systemie Arch i Ubuntu), że w Linuksie nie jest to konieczne ani już nie. W obu systemach instrukcja NAMEw bash jest po prostu NAMEnie \b.
terdon

@terdon Nie zauważyłem najpierw wzmianki o macOS, więc założyłem, że źle skonfigurowany system Linux jest możliwy. Odciąłem teraz bity Linuksa.
Kusalananda

Nic nie przeoczyłeś, zapytałem OP, jakiego systemu operacyjnego używają, ponieważ nie mogłem reprodukować w systemie Linux, powiedzieli macOS i właśnie go dodałem. I nie sugerowałem, że się myliłeś, bo wiem, że istnieją dystrybucje Linuksa, w których MAN_KEEP_FORMATTINGzmienna działa dokładnie tak, jak mówisz. Chciałem tylko zaznaczyć, że nie zawsze tak jest.
terdon
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.