Grep: resultados inesperados ao procurar palavras no cabeçalho da página man

18

Estou tendo um comportamento estranho ao tentar usar uma página de manual no macOS. Por exemplo, a página man do Bash claramente tem uma ocorrência da string NAME :

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

E se eu for grep para name , obtenho resultados, mas se eu for grep para NAME , não obtenho:

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

Já experimentei outras palavras em maiúsculas que conheço, e pesquisar por SHELL não produz nada, enquanto a pesquisa por BASH produz resultados.

O que está acontecendo aqui?

Atualização : Obrigado por todas as respostas! Eu pensei que vale a pena adicionar o contexto em que eu corri para isso. Eu queria escrever uma função bash para embrulhar man e, nos casos em que eu tentei procurar a página de manual para um shell interno, pule para a seção relevante da página man do Bash. Pode haver uma maneira melhor, mas aqui está o que eu tenho atualmente:

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
}
    
por ivan 14.06.2017 / 13:24

2 respostas

32

Se você adicionar um comando | sed -n l ao tail , para mostrar caracteres não imprimíveis, provavelmente verá algo como:

N\bNA\bAM\bME\bE

Ou seja, cada caractere é escrito como X Backspace X . Nos terminais modernos, o personagem acaba sendo escrito sobre si mesmo (como Backspace aka BS, também conhecido como \b aka ^H é o caractere que move o cursor uma coluna para a esquerda) sem diferença. Mas em tele-máquinas antigas, isso faria com que o personagem aparecesse em negrito, já que ele recebe o dobro de tinta.

Mesmo assim, os pagers como more / less entendem esse formato como negrito, então ainda é o que roff faz para gerar texto em negrito.

Algumas implementações do homem chamariam roff de uma maneira que essas seqüências não são usadas (ou internamente chamam col -b -p -x para removê-las como no caso da implementação man-db (a menos que a variável de ambiente MAN_KEEP_FORMATTING esteja definida) )), e não invoca um pager quando eles detectam que a saída não está indo para um terminal (então man bash | grep NAME iria trabalhar lá), mas não o seu.

Você pode usar col -b para remover essas seqüências (há outros tipos ( _ BS X ) também para sublinhado).

Para sistemas que usam GNU roff (como GNU ou FreeBSD), você pode evitar que essas sequências sejam usadas em primeiro lugar, assegurando que as opções -c -b -u sejam passadas para grotty , por exemplo, certificando-se de que -P-cbu options é passado para groff .

Por exemplo, criando um script de wrapper chamado groff contendo:

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

Que você ponha à frente de / usr / bin / groff em $PATH .

Com o macOS ' man (também usando o GNU roff ), você pode criar um man-no-overstrike.conf com:

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

E chame man como:

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

Ainda com o GNU roff , se você definir a variável de ambiente GROFF_SGR (ou não definir a variável GROFF_NO_SGR dependendo de como os padrões foram definidos em tempo de compilação), então grotty (contanto como não é passado a opção -c ) usará seqüências de escape de terminal ANSI SGR em vez desses truques BS para atributos de caractere. less os entende quando chamado com a opção -R .

O homem do FreeBSD chama grotty com a opção -c , a menos que você esteja solicitando cores configurando a variável MANCOLOR (caso em que -c não é passado para grotty e grotty reverte para o padrão de usar sequências de escape ANSI SGR lá).

MANCOLOR=1 man bash | grep NAME

funcionará lá.

No Debian, GROFF_SGR não é o padrão. Se você fizer:

GROFF_SGR=1 man bash | grep NAME

no entanto, como o stdout de man não é um terminal, ele exige que ele também passe uma variável GROFF_NO_SGR para grotty (suponho que ele possa usar col -bpx para remover as sequências BS col não sabe como remover as sequências SGR, mesmo que ainda o faça com MAN_KEEP_FORMATTING ), o que substitui nosso GROFF_SGR . Você pode fazer em vez disso:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(em um terminal) para ter as seqüências de escape do SGR.

Nesse momento, você notará que alguns desses NAME s aparecem em negrito no terminal (e em um less -R pager). Se você alimentar a saída para sed -n l ( MANPAGER='sed -n /NAME/l' ), verá algo como:

3[1mNAME3[0m$

Em que \e[1m é a sequência para ativar negrito em terminais compatíveis com ANSI e \e[0m a sequência para reverter todos os atributos SGR para o padrão.

Nesse texto grep NAME funciona como se o texto contivesse NAME , mas você ainda pode ter problemas se estiver procurando por texto em que apenas partes dele estão em negrito / sublinhado ...

    
por 14.06.2017 / 13:37
13

Se você olhar para qualquer página de manual, notará que os cabeçalhos estão em negrito. Isto é conseguido formatando-os com caracteres de controle. Para ser capaz de grep como você quer, elas precisam ser removidas.

O utilitário col pode ser usado para isso:

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

A opção -b tem a seguinte descrição no OpenBSD :

Do not output any backspaces, printing only the last character written to each column position. This can be useful in processing the output of mandoc(1).

Linux o manual col (no Ubuntu) não tem a última sentença (mas funciona da mesma maneira).

No Linux, desabilitar a variável de ambiente MAN_KEEP_FORMATTING (ou defini-la como uma string vazia) também pode ajudar e permitirá que você use grep sem passar a saída de man a col -b .

    
por 14.06.2017 / 13:31