Discarding the output of a process

I am seeking advice about the best way to discard the output of a process.

On Linux and BSD, package open-browser uses the xdg-open application script but discards its output. It does that with something that looks like this (the code comments document my understanding of what is going on):

openBrowser url =
  -- sh -c '...'
  --
  -- tells the shell (sh) to execute the given string as a command, and
  -- the following argument (url) is passed as a positional argument $0.
  --
  -- xdg-open "$0" 2>&1 > /dev/null
  --
  -- The $0 is expanded and the double quotes ensures it is treated as a single
  -- argument to xdg-open.
  --
  -- 2>&1 redirects standard error to standard output.
  --
  -- > /dev/null discards standard output.
  openBrowserWith "sh" ["-c", "xdg-open \"$0\" 2>&1 > /dev/null", url]

(openBrowserWith is a helper function that makes use of withCreateProcess.)

What I am wondering is why the original author made use of the sh shell (in July 2015, when GHC 7.10.1 was the latest thing) and did not just obtain, and then discard, the output from xdg-open in Haskell.

Is it possible to reason that one approach is to be preferred over the other?

EDIT: I’ve subsequently discovered§ that the shell approach discards the output of xdg-open but not of the application it opens while using withCreateProcess as follows:

openBrowserSilentlyWith ::
     String
     -- ^ Name of relevant executable on the PATH.
  -> [String]
     -- ^ Arguments for executable.
  -> IO Bool
openBrowserSilentlyWith cmd args =
  -- /dev/null is specifc to Unix-like operating systems
  withFile "/dev/null" WriteMode $ \nullHandle ->
    withCreateProcess (proc cmd args)
      { std_out = UseHandle nullHandle
      , std_err = UseHandle nullHandle
      } $ \_ _ _ p -> (== ExitSuccess) <$> waitForProcess p

discards the output of xdg-open and the application it opens.

That is because xdg-open is not an application but a script.

§ Discovered by experimenting, as google-chrome on Ubuntu 24.02.2, via WSL2, spits out all sorts of warnings when it opens.

It seems to be an intentional change between 0.1.3.0 and 0.1.4.0.

One potential difference between foo and sh -c foo is that in the second case foo is not necessarily an executable, but could be a shell command or a shell alias, but I struggle to see how it could be relevant for xdg-open.

1 Like

I think one other difference is that it now expands environment variables like $HOME.

1 Like

Thanks for the responses! In addition, @atravers has suggested avoiding Haskell’s garbage collection (GC) as a possible motive for using the shell. (GHC’s GC has moved on since the summer of 2015.)

A copy of the original (now deleted) repository suggests that the motivation for introducing the shell was ‘Silence xdg-open; Fixes [now lost] #7’. As it was introduced only for (chatty) xdg-open (Linux, BSD) and not (taciturn) open (macOS), it appears to me that the author’s own motivation was not shell expansion of environment variables.

EDIT: It turns out that xdg-open is not an application but a shell script (one that starts with a shebang - #!/bin/sh).

1 Like