38

I am not sure if this makes sense but I am thinking if there is a way to suppress the output shown for a command when run using the system method in ruby? I mean it should just output true or false to STDOUT and not the output of the command. What I think is it can just only be done if the command can run silently and not from the system method. Can someone provide a bit more insight?

Nakilon
  • 34,866
  • 14
  • 107
  • 142
kilari
  • 398
  • 1
  • 3
  • 4

8 Answers8

64

If you want to take advantage of the variadic form of Kernel.system, which side-steps the many quoting issues with shells, you can use the same options which Kernel.spawn accepts.

TL;DR - Use :out => File::NULL to silence output from Kernel.system

Arguments with special characters (spaces, etc.) can cause problems with the shell:

irb(main):001:0> filename_with_spaces = "foo bar.txt"
=> "foo bar.txt"

irb(main):002:0> system "ls -l #{filename_with_spaces}"
ls: bar.txt: No such file or directory
ls: foo: No such file or directory
=> false

So if you are interpolating variables into a system call, it is safer to provide the arguments separately:

irb(main):003:0> system "ls", "-l", filename_with_spaces
-rw-r--r--  1 nobody  nobody  9 Feb  1 16:53 foo bar.txt
=> true

But now we have a problem if we want to hide the output.

irb(main):004:0> system "ls", "-l", filename_with_spaces, "> /dev/null"
ls: > /dev/null: No such file or directory
-rw-r--r--  1 nobody  nobody  9 Feb  1 16:53 foo bar.txt
=> false

We could close STDOUT using the :out => :close option:

irb(main):005:0> system "ls", "-l", filename_with_spaces, :out => :close
=> true

However, this might cause issues with certain commands which may try to attach to STDOUT.

irb(main):006:0> system "echo", "hi there", :out => :close
echo: write: Bad file descriptor
=> false

To fix this, we can go back to redirecting our output, using File::NULL to remain portable:

irb(main):007:0> system "echo", "hi there", :out => File::NULL
=> true
Steve
  • 6,618
  • 3
  • 44
  • 42
  • 6
    From this answer I was able to deduce `:err =>` as well. Is there something that means both `out:` and `:err`, so I don’t have to write `:out => File::NULL, :err => File::NULL`? – user137369 Dec 07 '16 at 19:30
  • 3
    If you look at documentation for [Kernel#spawn](http://ruby-doc.org/core-2.3.0/Kernel.html#method-i-spawn), you'll find that you can do this `[:out, :err] => File::NULL`, as well as many other things. – Steve Dec 08 '16 at 00:32
  • @Steve Goddamn that's elegant AF. – Joshua Pinter Dec 29 '20 at 23:24
23

After a call to system the exit code is in the special variable $? so if useradd returns different values to indicate if the user was successfully added (e.g. 0 for success) then you can do the following:

system('useradd xx > /dev/null')
if $? == 0
  puts 'added'
else
  puts 'failed'
end

where the redirect to /dev/null will suppress the output.

Alternatively if the program being called does not use its exit code to indicate success or failure you can use backticks and search for a particular substring in the output e.g.

if `useradd xx`.include? 'success'
  puts 'it worked'
else
  puts 'failed to add user'
end
mikej
  • 65,295
  • 17
  • 152
  • 131
  • 10
    This alone won't suppress `stderr`. Should use this `system('useradd xx > /dev/null 2>&1')` – Dex Jan 09 '14 at 05:00
  • 1
    `system` also returns `true` or `false` and I especially like to depend on those simple values. The return values are not affected by the `> /dev/null 2>&1` redirection. – Ikon Jun 23 '15 at 09:26
  • 6
    None of these will work on Windows because `/dev/null` doesn't exist, even under Git-Bash. The better answer is to use `out: File::NULL, err: File::NULL`. That's properly portable and clearly indicates intent without having to worry about if you handled `&2>1` vs. `&>` vs whatever. – Rob Kinyon Sep 21 '16 at 15:02
8

As an addendum, I've been surprised a few times when I've used backticks and saw output "slipping past" my variables when running scripts from the command line.

Invariably, the issue is that the text I'm seeing is actually coming from stderr rather than stdout. So, to wrangle that text into stdout as well, remember to append 2>&1 to the command you're trying to run.

I hope that's helpful to someone. I just wasted twenty minutes re-learning this lesson :)

Chris Allen Lane
  • 6,334
  • 5
  • 25
  • 31
2

I was faced with this question as well...

Keep in mind that with ruby system calls, the UNIX command you are trying to use might offer a "silent" option--nullifying the need to suppress terminal output altogether!

For instance:

system 'curl -s "your_params_here"'

Will suppress the output that typically accompanies a curl call.

schro's cat
  • 119
  • 4
1

IO.popen

This is another good option:

IO.popen(['echo', 'a']) do |f|
  f.read == "a\n" or raise
end
$?.exitstatus == 0 or raise

Nothing will get output to your stdout.

http://www.ruby-doc.org/core-2.1.4/IO.html#method-c-popen

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0
  • define null_device, it's operating systems dependent: windows 7 and newer use nul, while *nix systems uses /dev/null
  • run the command
  • redirect it's standard and error output to null_device
  • then use the exit code, or the backtick method as mentioned by @mikej to determine the output

as follows:

 null_device = Gem.win_platform? ? "/nul" : "/dev/null"

 do method
   system "run command 1>#{null_device} 2>#{null_device} "
   p ($? == 0)
 end
Community
  • 1
  • 1
yaitloutou
  • 1,691
  • 19
  • 23
-2

You can also use backticks or %x

Azeem.Butt
  • 5,855
  • 1
  • 26
  • 22
-4

This should work

system ls, STDOUT:'/dev/null'
Undo
  • 25,519
  • 37
  • 106
  • 129
William Entriken
  • 37,208
  • 23
  • 149
  • 195