1

I've been working on a script with a variety of functions.

I'm trying to add a function that show's the output of the whatis command for all applications in the "/usr/share/applications" folder, without showing lines that say "nothing appropriate".

My current command attempt is:

whatis $(ls /usr/share/applications | cut -d "." -f 1) | grep -i -v "nothing appropriate"

The problem I'm having is that the grep command doesn't seem to be functioning, so lines containing the phrase "nothing appropriate" are still showing.

I've also tried a number of alternatives, including using the "replace" and "sed" commands to convert the unwanted lines into empty space, but those have also failed to solve the issue.

All of the aforementioned attempts, I've tried for the command output directly, and I've also tried routing the output into a file, and executing the filters on the "cat" output of the file.

Does anyone have any alternatives they can recommend?

Lee
  • 105
  • 10

1 Answers1

1

Error messages are usually written to stderr, and you're only piping stdout to grep. To pipe both stdout and stderr, in Bourne-like shells of fish, you do:

whatis... 2>&1 | grep -v 'nothing appropriate'

Note that it means stdout and stderr end up being merged on stdout.

The shell in there sees a pipeline, so it splits it into whatis... 2>&1 and grep -v 'nothing appropriate' and plumbs them with the stdout (file descriptor 1) of the left one being the writing end of a pipe and the stdin (fd 0) of the right one being the other end of that pipe, each side running concurrently in separate processes.

On each side, the redirections are processed. 2>&1 says: redirect fd 2 to the same thing as opened on fd 1, here the pipe. So both fd 1 and 2 go to the pipe.

The equivalent syntax for rc-like shells is whatis... >[2=1] | grep... and with csh-like shells: whatis... |& grep... (also works in zsh).

With ksh93, zsh or bash, you can also send stderr only to a filtering pipe with:

whatis... 2> >(grep -v 'nothing appropriate' >&2)

But note that that grep command is not waited for so you may end-up seeing the other error messages after the command has returned. That can be avoided in zsh with:

{whatis...} 2> >(grep -v 'nothing appropriate' >&2)

(or more painfully in ksh93 and bash).

In yash, you can use process redirection (with similar syntax as process substitution, but different in that it's a redirection operator, not an expansion), with the same problem wrt the filtering command not being waited for:

whatis... 2>(grep -v 'nothing appropriate' >&2)

Beware that with cut -d "." -f 1 you end up changing org.gnome.Settings.desktop to org instead of org.gnome.Settings. Because you're invoking split+glob, you'll also have problems for application names that contain characters of $IFS or glob characters.

With zsh, you'd do:

whatis -- /usr/share/applications/*.desktop(:t:r) |& grep -v 'nothing appropriate'

Where :t takes the tail (dirname) of the file and :r the root name (without extension) and |& is short for 2>&1 |.

In bash, you can do something approaching with:

(
  shopt -s failglob
  set /usr/share/applications/*.desktop
  set -- "${@##*/}"
  whatis -- "${@%.*}" 2>&1
) | grep -v 'nothing appropriate'

Where "${@##*/}" does the equivalent of :t, "${@%.*}" the equivalent of :r.

With GNU ls 9.0 or newer, you can also do:

ls --zero /usr/share/applications/ |
  LC_ALL=C sed -zn 's/\.desktop$//p' |
  xargs -r0 whatis -- 2>&1 |
  grep -v 'nothing appropriate'

Where ls --zero outputs the list in a post-processable format, sed removes the .desktop extension and outputs the result if there was a substitution (therefore excluding files without .desktop extension), xargs passes the list the whatis, and grep filters out the nothing appropriate errors.

With GNU basename, you can also do:

(
  shopt -s failglob
  basename -zs .desktop /usr/share/applications/*.desktop
) |
  xargs -r0 whatis -- 2>&1 |
  grep -v 'nothing appropriate'

Also beware that the name of the desktop files often don't match the name of the executable they execute if any and so are likely to not have a corresponding man page.

Instead of passing the rootname to whatis, you could also extract some fields of those .desktop files. For instance:

(cd /usr/share/applications && 
  grep -HPom1 -e '^GenericName=\K.*' -- *.desktop
)

Would extract the value of the first occurrence of a GenericName field in those files.

  • Thank you Stephane. You've solved the issue. The final command is:
    whatis $(ls /usr/share/applications | cut -d "." -f 1) 2>&1 | grep -i -v "nothing appropriate"
    – Lee Oct 01 '22 at 16:32
  • @Lee, beware that as I said, with that, for the io.snapcraft.SessionAgent.desktop file for instance, you end up doing whatis io and could get IO (3perl) - load various IO modules for that which is unrelated. See the rest of my answer for more reliable ways to remove the .desktop extension. – Stéphane Chazelas Oct 01 '22 at 16:39
  • I'll be factoring those things in as I proceed. The original issue has been solved, it's just a matter of simple text manipulation at this point :) – Lee Oct 01 '22 at 16:55