Grep: risultati imprevisti durante la ricerca di parole nell'intestazione dalla pagina man


19

Sto riscontrando un comportamento strano quando provo ad accedere a una pagina man su macOS. Ad esempio, la pagina man di Bash ha chiaramente una ricorrenza della stringa NAME:

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

E se grep per nameottengo risultati, ma se grep per NAMEnon lo faccio:

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

Ho provato altre parole maiuscole che so essere lì dentro, e la ricerca di SHELLnon BASHproduce risultati mentre la ricerca di risultati di risultati.

Cosa sta succedendo qui?

Aggiornamento : grazie per tutte le risposte! Ho pensato che valesse la pena aggiungere il contesto in cui mi sono imbattuto in questo. Volevo scrivere una funzione bash da avvolgere mane nei casi in cui ho cercato di cercare nella pagina man un built-in di shell, passare alla sezione pertinente della pagina man di Bash. Potrebbe esserci un modo migliore, ma ecco quello che ho attualmente:

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
}


Quale sistema operativo stai usando? Sono sicuro che la risposta accettata è corretta, ma IO non è riuscito a riprodurla sulla mia casella Arch Linux. man bash | grep NAMEfunziona come previsto.
Terdon

@terdon, sono su macOS. Ottengo questo comportamento con Bash 3.2 e 4.4.5
ivan,

A parte questo: se rilevi un builtin, puoi semplicemente usare il helpcomando bash per ottenere le sue informazioni.
Joe,

@Joe Il problema è che spesso trovo che i helprisultati tralascino troppo. Dai un'occhiata help completealla completesezione in man bash, per esempio.
Ivan

Risposte:


33

Se aggiungi | sed -n la quel tailcomando, per mostrare caratteri non stampabili, probabilmente vedrai qualcosa di simile:

N\bNA\bAM\bME\bE

Cioè, ogni personaggio è scritto come XBackspace X. Sui terminali moderni, il personaggio finisce per essere scritto su se stesso (poiché Backspace aka BS aka \baka ^Hè il personaggio che sposta il cursore di una colonna a sinistra) senza alcuna differenza. Ma negli antichi tele-macchine da scrivere, ciò farebbe apparire il personaggio in grassetto poiché ottiene il doppio di inchiostro.

Tuttavia, ai cercapersone piace more/ lesscapisce che quel formato significhi grassetto, quindi è ancora ciò che rofffa produrre grassetto.

Alcune implementazioni man chiamerebbero roffin modo tale che quelle sequenze non vengano utilizzate (o chiamate internamente col -b -p -xper eliminarle come nel caso man-dbdell'implementazione (a meno che non MAN_KEEP_FORMATTINGsia impostata la variabile d'ambiente)) e non invochino un cercapersone quando rilevano l'output non sta andando a un terminale (quindi man bash | grep NAMEfunzionerebbe lì), ma non il tuo.

Puoi usare col -bper rimuovere quelle sequenze (ci sono anche altri tipi ( _BS X) per sottolineare).

Per i sistemi che usano GNU roff(come GNU o FreeBSD), puoi evitare di usare quelle sequenze in primo luogo assicurandoti che le -c -b -uopzioni siano passate grotty, ad esempio assicurandoti che le -P-cbuopzioni siano passate a groff.

Ad esempio creando uno script wrapper chiamato groffcontenente:

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

Che hai inserito in / usr / bin / groff $PATH.

Con macOS ' man(anche usando GNU roff), puoi creare un man-no-overstrike.confcon:

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

E chiama mancome:

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

Sempre con GNU roff, se si imposta la GROFF_SGRvariabile di ambiente (o non si imposta la GROFF_NO_SGRvariabile a seconda di come sono stati impostati i valori predefiniti al momento della compilazione), allora grotty(purché non venga passata l' -copzione) si useranno invece le sequenze di escape del terminale SGR ANSI di quei trucchi BS per gli attributi dei personaggi. lesscapirli quando viene chiamato con l' -Ropzione.

L'uomo di FreeBSD chiama grottycon l' -copzione a meno che tu non stia chiedendo i colori impostando la variabile MANCOLOR (nel qual caso -cnon viene passato grottye grottyritorna all'impostazione predefinita usando le sequenze di escape ANSI SGR).

MANCOLOR=1 man bash | grep NAME

lavorerà lì.

Su Debian, GROFF_SGR non è l'impostazione predefinita. Se fate:

GROFF_SGR=1 man bash | grep NAME

tuttavia, poiché lo manstdout non è un terminale, si assume anche il compito di passare una GROFF_NO_SGRvariabile a grotty(suppongo che possa essere utilizzata col -bpxper rimuovere le sequenze BS poiché colnon sa come rimuovere le sequenze SGR, anche se è ancora lo fa con MAN_KEEP_FORMATTING) che sovrascrive il nostro GROFF_SGR. Puoi invece fare:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(in un terminale) per avere le sequenze di escape SGR.

Quella volta, noterai che alcuni di questi NAME appaiono in grassetto sul terminale (e in un less -Rcercapersone). Se dai l'output a sed -n l( MANPAGER='sed -n /NAME/l'), vedrai qualcosa di simile a:

\033[1mNAME\033[0m$

Dov'è \e[1mla sequenza per abilitare il grassetto nei terminali compatibili ANSI e \e[0mla sequenza per ripristinare tutti gli attributi SGR sui valori predefiniti.

Su quel testo grep NAMEfunziona come quel testo contiene NAME, ma potresti ancora avere problemi se cerchi un testo in cui solo parti di esso sono in grassetto / sottolineato ...


2
Wow, abbastanza interessante vedere l'eredità del tele-tipo fisico lì. Il doppio di inchiostro => grassetto. Ha perfettamente senso
ivan

1
Sto amando sed -n lcome un sostituto per od.
Tom Hale,

13

Se guardi qualsiasi pagina di manuale, noterai che le intestazioni sono in grassetto. Ciò si ottiene formattandoli con caratteri di controllo. Per poter greppiacere come vuoi, questi devono essere eliminati.

L' colutilità può essere utilizzata per questo:

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

L' -bopzione ha la seguente descrizione su OpenBSD :

Non generare alcun backspaces, stampando solo l'ultimo carattere scritto in ogni posizione di colonna. Ciò può essere utile per elaborare l'output di mandoc (1).


Linux il colmanuale (su Ubuntu) non contiene l'ultima frase (ma funziona allo stesso modo).

Su Linux, anche annullare l' MAN_KEEP_FORMATTINGimpostazione della variabile di ambiente (o impostarla su una stringa vuota) può essere di aiuto e ti consentirà di grepnon passare l'output di manthrough col -b.


Penso (come in questo ho provato su un sistema Arch e Ubuntu) che su Linux questo non è necessario, o non più. Su entrambi i sistemi, il NAMEmanuale di bash è giusto NAME, no \b.
terdon

@terdon Non ho notato prima la menzione di macOS, quindi ho ipotizzato che un sistema Linux configurato erroneamente fosse una possibilità. Ora ho eliminato i bit di Linux.
Kusalananda

Non ti sei perso nulla, ho chiesto all'OP quale sistema operativo stavano usando perché non potevo riprodurre su Linux, hanno detto macOS e l'ho appena aggiunto ora. E non intendevo dire che avessi torto, per quanto ne so ci sono distribuzioni Linux là fuori dove la MAN_KEEP_FORMATTINGvariabile funziona esattamente come dici tu. Volevo solo sottolineare che non è sempre così.
terdon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.