Search for man pages which contain ALL of the words 'foo' 'bar' and 'baz'

I implemented a script that does exactly this.

if [ $# -eq 0 ]; then
  PATTERNS=(NAME AUTHOR EXAMPLES FILES)
else
  PATTERNS=( "$@" )
fi

[ ${#PATTERNS[@]} -lt 1 ] && echo "Needs at least 1 pattern to search for" && exit 1

for i in $(find /usr/share/man/ -type f); do
  TMPOUT=$(zgrep -l "${PATTERNS[0]}" "$i")
  [ -z "$TMPOUT" ] && continue

  for c in `seq 1 $((${#PATTERNS[@]}-1))`; do
    TMPOUT=$(echo "$TMPOUT" | xargs zgrep -l "${PATTERNS[$c]}")
    [ -z "$TMPOUT" ] && break
  done

  if [ ! -z "$TMPOUT" ]; then
    #echo "$TMPOUT" # Prints the whole path
    MANNAME="$(basename "$TMPOUT")"
    man "${MANNAME%%.*}"
  fi
done

Guess it was a waste of time :(

Edit: Seems like

man -K expr1 expr2 expr3

didn't work?

Edit: You can pass the scripts now your search terms via ./script foo bar


A few thoughts on scripting this:

  • Using manpath to get the location(s) of the man pages. If I add /home/graeme/.cabal/bin to my PATH, manpath (and man) will find man pages in /home/graeme/.cabal/share/man.

  • Use man itself to decompress and format the pages before searching, this way you are just searching the man text itself and not any comments etc in the raw file. Using man will potentially deal with multiple formats.

  • Saving the formatted pages in a tempfile will avoid multiple decompressions and should speed things up significantly.

Here goes (with bash and GNU find):

#!/bin/bash

set -f; IFS=:
trap 'rm -f "$temp"' EXIT
temp=$(mktemp --tmpdir search_man.XXXXXXXXXX)

while IFS= read -rd '' file; do
  man "$file" >"$temp" 2>/dev/null

  unset fail
  for arg; do
    if ! grep -Fq -- "$arg" "$temp"; then
      fail=true
      break
    fi
  done

  if [ -z "$fail" ]; then
    file=${file##*/}
    printf '%s\n' "${file%.gz}"
  fi
done < <(find $(manpath) -type d ! -name 'man*' -prune -o -type f -print0)

Not as complete as @polym's answer, but I was going to suggest something like

while IFS= read -rd $'\0' f; do 
  zgrep -qwm1 'foo' "$f" && \
  zgrep -qwm1 'bar' "$f" && \
  zgrep -qwm1 'baz' "$f" && \
  printf '%s\n' "$f"
done < <(find /usr/share/man -name '*.gz' -print0)

Note that I added a -w (word match) switch to the greps - which may not be what you want (do you want to include matches like foolish and nutbar?)

Tags:

Search

Man