Grep: resultados inesperados al buscar palabras en el encabezado de la página de manual

19

Me encuentro con un comportamiento extraño al intentar grep una página de manual en macOS. Por ejemplo, la página de manual de Bash claramente tiene una aparición de la cadena NAME:

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

Y si busco name, obtengo resultados, pero si NAMEbusco, no:

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

He intentado otras palabras en mayúscula que sé que están allí, y la búsqueda de SHELLresultados no produce nada mientras que la búsqueda de BASHresultados arroja resultados.

¿Que está pasando aqui?

Actualización : ¡Gracias por todas las respuestas! Pensé que valía la pena agregar el contexto en el que me encontré con esto. Quería escribir una función bash para ajustar many, en los casos en que he intentado buscar la página del manual para un shell incorporado, salte a la sección correspondiente de la página del manual de Bash. Puede haber una mejor manera, pero esto es lo que tengo actualmente:

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
}
ivan
fuente
Qué sistema operativo estás usando? Estoy seguro de que la respuesta aceptada es correcta, pero IO no pudo reproducir esto en mi caja Arch Linux. man bash | grep NAMEFunciona como se esperaba.
terdon
@terdon Estoy en macOS. Obtengo este comportamiento con Bash 3.2 y 4.4.5
ivan
Solo como un aparte: si detecta un incorporado, puede usar el helpcomando bash para obtener su información.
Joe
@ Joe El problema es que a menudo encuentro que los helpresultados dejan demasiado afuera. Echa un vistazo a help completela completesección en man bash, por ejemplo.
ivan

Respuestas:

33

Si agrega un comando | sed -n la ese tailcomando, para mostrar caracteres no imprimibles, probablemente verá algo como:

N\bNA\bAM\bME\bE

Es decir, cada personaje está escrito como XRetroceso X. En las terminales modernas, el personaje termina siendo escrito sobre sí mismo (como Backspace aka BS aka \baka ^Hes el personaje que mueve el cursor una columna hacia la izquierda) sin diferencia. Pero en las máquinas de escribir antiguas, eso haría que el personaje apareciera en negrita, ya que recibe el doble de tinta.

Aún así, los localizadores como more/ lessentienden que el formato significa negrita, por lo que sigue siendo eso rofflo que hace que el texto aparezca en negrita.

Algunas implementaciones de hombre llamarían roffde una manera que esas secuencias no se usan (o llamarían internamente col -b -p -xpara despojarlas como en el caso de la man-dbimplementación (a menos que se establezca la MAN_KEEP_FORMATTINGvariable de entorno)), y no invoquen un buscapersonas cuando detecten la salida no va a una terminal (por man bash | grep NAMElo que funcionaría allí), pero no a la suya.

Puede usar col -bpara eliminar esas secuencias (también hay otros tipos ( _BS X) para subrayar).

Para los sistemas que usan GNU roff(como GNU o FreeBSD), puede evitar que esas secuencias se usen en primer lugar asegurándose de -c -b -uque se pasen las opciones grotty, por ejemplo, asegurándose de -P-cbuque se pasen las opciones groff.

Por ejemplo, creando una secuencia de comandos de contenedor llamada que groffcontiene:

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

Que pones delante de / usr / bin / groff $PATH.

Con macOS ' man(también usando GNU roff), puede crear un man-no-overstrike.confcon:

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

Y llama mancomo:

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

Aún con GNU roff, si establece la GROFF_SGRvariable de entorno (o no establece la GROFF_NO_SGRvariable dependiendo de cómo se hayan establecido los valores predeterminados en el momento de la compilación), entonces grotty(siempre que no se pase la -copción) utilizará secuencias de escape de terminal ANSI SGR en su lugar de esos trucos BS para atributos de personaje. lessComprenderlos cuando se llama con la -Ropción.

El hombre de FreeBSD llama grottycon la -copción a menos que esté pidiendo colores configurando la variable MANCOLOR (en cuyo caso -cno se pasa grottyy grottyvuelve al valor predeterminado de usar secuencias de escape ANSI SGR allí).

MANCOLOR=1 man bash | grep NAME

Trabajaré allí.

En Debian, GROFF_SGR no es el valor predeterminado. Si lo haces:

GROFF_SGR=1 man bash | grep NAME

sin embargo, debido a que manstdout no es una terminal, se encarga de pasar también una GROFF_NO_SGRvariable a grotty(supongo que puede usarse col -bpxpara eliminar las secuencias BS ya colque no sabe cómo eliminar las secuencias SGR, aunque todavía lo hace con MAN_KEEP_FORMATTING) que anula nuestra GROFF_SGR. Puedes hacer en su lugar:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(en un terminal) para tener las secuencias de escape SGR.

Esa vez, notará que algunos de esos NOMBRES aparecen en negrita en el terminal (y en un less -Rbuscapersonas). Si alimenta la salida a sed -n l( MANPAGER='sed -n /NAME/l'), verá algo como:

\033[1mNAME\033[0m$

¿Dónde \e[1mestá la secuencia para habilitar negrita en terminales compatibles con ANSI y \e[0mla secuencia para revertir todos los atributos SGR al valor predeterminado?

En ese texto grep NAMEfunciona como ese texto contiene NAME, pero aún podría tener problemas si busca texto donde solo partes de él están en negrita / subrayado ...

Stéphane Chazelas
fuente
2
Wow, es bastante interesante ver el legado del tele-tipo físico allí. El doble de tinta => negrita. Tiene mucho sentido
ivan
1
Estoy amando sed -n lcomo un sustituto de od.
Tom Hale
13

Si observa cualquier página del manual, notará que los encabezados están en negrita. Esto se logra formateándolos con caracteres de control. Para poder grepgustar lo que quieres, estos deben ser eliminados.

La colutilidad se puede usar para esto:

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

La -bopción tiene la siguiente descripción en OpenBSD :

No imprima ningún espacio de retroceso, imprimiendo solo el último carácter escrito en cada posición de columna. Esto puede ser útil para procesar la salida de mandoc (1).


Linux el colmanual (en Ubuntu) no tiene la última oración allí (pero funciona de la misma manera).

En Linux, desarmar la MAN_KEEP_FORMATTINGvariable de entorno (o configurarlo en una cadena vacía) también puede ayudar, y le permitirá hacerlo grepsin pasar la salida de a mantravés col -b.

Kusalananda
fuente
Creo (como probé esto en un sistema Arch y Ubuntu) que en Linux esto ya no es necesario, o ya no. En ambos sistemas, el NAMEmanual de bash es simplemente NAMEno \b.
terdon
@terdon No vi la mención de macOS primero, así que asumí que un sistema Linux mal configurado era una posibilidad. Ahora he recortado los bits de Linux.
Kusalananda
No te perdiste nada, le pregunté al OP qué sistema operativo están usando porque no pude reproducir en Linux, dijeron macOS y lo acabo de agregar ahora. Y no estaba insinuando que estabas equivocado, por lo que sé, hay distribuciones de Linux por ahí donde la MAN_KEEP_FORMATTINGvariable funciona exactamente como tú dices. Solo quería señalar que ese no es siempre el caso.
terdon