1

I have a manpage, and I want to find something in a specific section. Grep is having a hard time, and I figured out the issue was headings have control characters in them, e.g.

SEE ALSO

is really:

S^HSE^HEE^HE A^HAL^HLS^HSO^HO$

(thank you, cat -e, learned that one today as well)

Been playing with many variants of grep & sed, using character classes and other techniques and just haven't been able to nail it.

Any suggestions how best to return, say "everything in the SEE ALSO section"? That should be generic enough to be useful to others, but specific enough for a detailed answer. :)

Note that I don't actually want help with man, because in this case, the output is getting generated by another command, like say aws help which pipes through to less.

  • 1
    Manpages usually use backspaces for the bold, but that's not necessaily true for other commands, which would use terminal escape codes to generate bold, colours, etc. – muru Nov 18 '15 at 23:14
  • @muru, I'd like to officially say that's odd :) – halr9000 Nov 19 '15 at 22:11
3

The idea how to get plain version of man pages one can find in man man:

man foo | col -b

Based on that you can can get only one section for example with pcregrep:

man man | col -b | pcregrep -Mo '^SEE ALSO(.|\n)*?^[^ ]'

You can adjust it a little bit and put into function to grep in any section of any manual easily:

gsman () { man $1 | col -b | pcregrep -iMo "^$2(.|\n)*?(?=\n[A-Z])" ; }

and usage would be

gsman grep options | grep invert
2

A hex dumper (hexdump, xxd) and ascii(7) may be of utility here:

man ls | hexdump -C
...

Which shows:

000045f0  35 29 2e 0a 0a 53 08 53  45 08 45 45 08 45 20 41  |5)...S.SE.EE.E A|
00004600  08 41 4c 08 4c 53 08 53  4f 08 4f 0a 20 20 20 20  |.AL.LS.SO.O.    |

So that's hex code 8, or a bunch of bs (which is also what the ^H shown by cat means). As for how to strip the bs, there's a variety of ways:

% man ls | perl -ple 'tr/\x08//d' | grep SEE
SSEEEE AALLSSOO

Well that's no good, have to also get rid of the character being back spaced over:

% man ls | perl -ple 's/.\x08//g' | perl -00 -nle 'print if m/SEE ALSO/'
SEE ALSO
     chflags(1), chmod(1), sort(1), xterm(1), compat(5), termcap(5),
     symlink(7), sticky(8)
1
section="SEE ALSO"
regex=$(sed 's/./&.*/g' <<<"$section")       # S.*E.*E.* .*A.*L.*S.*O.*
# then
some help command | sed -n '/^'"$regex"'/,/^[^[:blank:]]/ p' | sed '$d'
  • The regular expression allows any number of characters in between each letter of the given section name.
  • the first sed command only outputs the lines between the given section regex and the next section (I'm assuming that all lines in the section have leading whitespace)
  • the second sed command removes the trailing section title.

As a bash function:

man_section() {
    local section=$1
    local regex=$(sed 's/./&.*/g' <<<"$section")
    sed -n '/^'"$regex"'/,/^[^[:blank:]]/ p' | sed '$d'
}

some help command | man_section "SEE ALSO"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.