0

I want make this below code works for Swift, the codes works okay without /dev/null 2>&1, as soon as I add /dev/null 2>&1, it does not work, how can I add it correctly?

let task = Process()
task.executableURL = URL(fileURLWithPath: "/bin/bash/dev/null 2>&1")
task.arguments = ["-c", """
                    echo "Hello, world!"
                    cd ${HOME}
                    cd Desktop
                    mkdir "New Folder"
                    echo "Folder is Successfully created!"
                    """]
task.launch()
ios coder
  • 1
  • 4
  • 31
  • 91
  • It's unclear. `2>&1` means put error output (stderr) in normal output (stdout). So how could ""/bin/bash/dev/null 2>&1"" be a file path? If In `task`, if you don't read the output, what about checking the output, but not print it? Same for error one. – Larme Jan 22 '22 at 20:51
  • @Larme: I am not advanced in Bash, but as far as I now `2>&1` should mute error in case. Also as I mentioned in my question, I am asking the correct way of use case, I just add it by myself. Need to learn the right way plz. – ios coder Jan 22 '22 at 21:06
  • @ioscoder Is the script you have here just an example script, or are you actually looking to create a new directory at `$HOME/New Folder`? Because if it's the latter, you don't need to be using `Process` or `bash` for this at all: [`FileManager`](https://stackoverflow.com/questions/70816981/how-can-i-stop-printing-terminal-in-swift) would be the right way to do that. – Itai Ferber Jan 22 '22 at 22:20
  • @ItaiFerber: thanks a lot, but I want it for bash purpose in swift. There is some time I want mute print. – ios coder Jan 22 '22 at 22:21

1 Answers1

1

Two main issues here:

  1. The executable URL to the binary you'd like to run needs to point to the binary — it can't take arguments, because /bin/bash/dev/null 2>&1 is not a valid path on disk. If you want to run /bin/bash, the executable URL can only contain /bin/bash
  2. The redirection you're trying to perform isn't written out quite correctly either. Briefly:
    • 2>&1 in bash means "please point stderr (2) to where stdout (1) is currently pointing"
    • In order to redirect stdout first, it's not enough to just specify /dev/null — you need to tell bash "please point stdout to /dev/null", which is done with the shorthand >/dev/null (note the leading > meaning "redirection")

The syntax for these redirections is part of the actual shell you're running, and although bash might understand >/dev/null, this won't work for all processes.

One way you can request this redirection of bash is to include the redirection request inside of the shell script you're running (i.e., what you pass in to -c). This starts getting into the specifics of bash and how it handles redirections, but an example of how to achieve this:

task.arguments = ["-c", """
                        exec >/dev/null 2>&1
                        # The rest of your script.
                        """]

That exec line causes bash to effect the redirection, and it remains active for the rest of the script. Explaining the specifics might be out of scope, but check out How to redirect output of an entire shell script within the script itself? for more information.

However:

Instead of this bash-specific approach, a less fragile and more expressive way to achieve exactly this is to explicitly set the .standardOutput and .standardError properties on the Process itself. You can redirect stdout and stderr from outside of bash altogether with the following:

let task = Process()
task.executableURL = URL(fileURLWithPath: "/bin/bash")
task.arguments = ["-c", "..."]
task.standardOutput = FileHandle.nullDevice // Equivalent to /dev/null
task.standardError = FileHandle.nullDevice
task.launch()

.standardInput, .standardOutput, and .standardError on a Process help direct where the process's input comes from, and where its output goes. By default, those are inherited from your current process (which is why you're even seeing bash's output in your own output to begin with), but you can assign a FileHandle to write the output to a file, or set a Pipe to be able to read the output directly.

The above redirection will work for any process, not just bash, and it's recommended you use these semantic properties over relying on bash parsing.


If you don't care about redirecting to /dev/null specifically, you can alternatively close the process's stdout and stderr by passing in nil instead of FileHandle.nullDevice:

task.standardOutput = nil
task.standardError = nil

For this specific application, this assignment will have the same effect as redirecting to /dev/null: output from your script will be silenced.

There is technically a difference between these (if the script were inspecting where its stdout and stderr were being output to), but in the most common use cases, this would be equivalent. Consider using this as a "simplification" over referring to FileHandle.nullDevice directly.

Itai Ferber
  • 28,308
  • 5
  • 77
  • 83
  • Thanks for helping me but the code in first block about `task.arguments = [">/dev/null", "2>&1", "-c", "..."]` does not help much Swift error: `/bin/bash: >/dev/null: No such file or directory` – ios coder Jan 22 '22 at 23:54
  • @ioscoder On my machine it appears to work, but intermittently. I would highly recommend using `.standardOutput` and `.standardError`, and I think I'll remove that initial suggestion altogether. – Itai Ferber Jan 23 '22 at 00:02
  • Your answer solve my issue, how ever I wanted to learn how i can use `/dev/null 2>&1` correctly with swift. – ios coder Jan 23 '22 at 00:06
  • @ioscoder I'll update my answer to include a bit more information, but the short of it is that "you can't", with that syntax. Specifically, `>/dev/null` and `2>&1` are not Swift syntax, but `bash` syntax; you need to pass those in in a way that `bash` itself understands, because those don't mean anything to Swift. One alternative way is passing `exec >/dev/null 2>&1` as a command at the very top of your script (right after `"""`) — this will cause `bash` to execute that redirection inside of the script itself. – Itai Ferber Jan 23 '22 at 00:18
  • 1
    @ioscoder For more info on that approach, https://stackoverflow.com/questions/314675/how-to-redirect-output-of-an-entire-shell-script-within-the-script-itself might help with some specifics. – Itai Ferber Jan 23 '22 at 00:18
  • 1
    Thanks, I will try to use `exec >/dev/null 2>&1`, your answer is completely solved my issue I just needed more learning how could I use `>/dev/null 2>&1`, update: I just tried and it works, thanks again. :) – ios coder Jan 23 '22 at 00:29