[RCR/BUG?] fix system

this seems broken:

   ~/tmp > cat a.rb
   STDERR.reopen STDOUT
   STDOUT.sync
   DIV = '=' * 79

   commands = [
     'no-such-command',
     'ls --no-such-option',
     'ls .',
   ]

   commands.each do |command|
     [false, true].each do |redirect|
       fork do
         puts DIV

         command << " >/dev/null 2>&1" if redirect

         puts "command <#{ command }>"

         system command

         puts "exit_status <#{ $?.exitstatus }>"
       end
       Process.wait
     end
   end

   ~/tmp > ruby a.rb

···

===============================================================================
   command <no-such-command>
   a.rb:18:in `system': No such file or directory - no-such-command (Errno::ENOENT)
           from a.rb:18
           from a.rb:13:in `fork'
           from a.rb:13
           from a.rb:13:in `each'
           from a.rb:12
           from a.rb:12:in `each'
           from a.rb:11
   ===============================================================================
   command <no-such-command >/dev/null 2>&1>
   exit_status <127>
   ===============================================================================
   command <ls --no-such-option>
   ls: unrecognized option `--no-such-option'
   Try `ls --help' for more information.
   exit_status <1>
   ===============================================================================
   command <ls --no-such-option >/dev/null 2>&1>
   exit_status <1>
   ===============================================================================
   command <ls .>
   a.rb
   exit_status <0>
   ===============================================================================
   command <ls . >/dev/null 2>&1>
   exit_status <0>

the above behaviour of system means the following rules must be followed when
making system calls:

   - iff stderr IS NOT redirected you must be prepared to catch Errno::ENOENT in
     the event your command is not found.

   - iff stderr IS redirected you will NOT be informed of non-existent commands
     via an exception and must check $?.exitstatus manually for 127 to
     determine if the command was not found

   - in all cases you must check $?.exitstatus for success since your command
     may be found, but fail for other reasons and this will not raise any
     errors.

so, in summary you must still always check $?, but sometimes errors will be
thrown depending on the contents of the string passed to system. am i the
only one who finds this confusing? it seems like any code using system will
turn into spaghetti if it follows these rules...

-a
--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Oh dear...

Is this a snapshot?

···

On Tuesday 29 Jun 2004 18:22, Ara.T.Howard wrote:

so, in summary you must still always check $?, but sometimes errors will be
thrown depending on the contents of the string passed to system. am i the
only one who finds this confusing? it seems like any code using system
will turn into spaghetti if it follows these rules...

Hi,

the above behaviour of system means the following rules must be followed when
making system calls:

If you put the shell special characters ("*?{}<>()~&|\\$;'`\"\n") in
the command string, system() will prepend "sh -c" to the command string,
so that

  no-such-command

was interpreted as

  sh -c no-such-command

that explains the behavior.

              matz.

···

In message "[RCR/BUG?] fix system" on 04/06/30, "Ara.T.Howard" <Ara.T.Howard@noaa.gov> writes:

Hi,

At Wed, 30 Jun 2004 02:22:54 +0900,
Ara.T.Howard wrote in [ruby-talk:104856]:

   - iff stderr IS NOT redirected you must be prepared to catch Errno::ENOENT in
     the event your command is not found.

   - iff stderr IS redirected you will NOT be informed of non-existent commands
     via an exception and must check $?.exitstatus manually for 127 to
     determine if the command was not found

Even with stdin or stdout. And you should remember that
redirecting stderr is not portable.

   - in all cases you must check $?.exitstatus for success since your command
     may be found, but fail for other reasons and this will not raise any
     errors.

Kernel#system returns true when the command succeeded, or false.

One thought:
system could return nil keeping error info when the command
couldn't execute, as perl does. Then, the current behavior
would moved to Process.start, invoke or something else.

···

--
Nobu Nakada

yes.

~/build/ruby > ruby -v
ruby 1.9.0 (2004-05-27) [i686-linux]

-a

···

On Wed, 30 Jun 2004, Andrew Walrond wrote:

On Tuesday 29 Jun 2004 18:22, Ara.T.Howard wrote:

so, in summary you must still always check $?, but sometimes errors will be
thrown depending on the contents of the string passed to system. am i the
only one who finds this confusing? it seems like any code using system
will turn into spaghetti if it follows these rules...

Oh dear...

Is this a snapshot?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Should have remembered that :slight_smile:
But the throwing exceptions thing is still new, as discussed in another
thread.

Andrew

···

On Wednesday 30 Jun 2004 00:25, Yukihiro Matsumoto wrote:

If you put the shell special characters ("*?{}<>()~&|\\$;'`\"\n") in
the command string, system() will prepend "sh -c" to the command string,
so that

that explains the behavior.

yes it does... i guess it's just a little odd when combined with the
exception throwing through... so, the reccomended policy is to be prepared to
deal with both exceptions and $? - correct?

cheers.

-a

···

On Wed, 30 Jun 2004, Yukihiro Matsumoto wrote:

Hi,

In message "[RCR/BUG?] fix system" > on 04/06/30, "Ara.T.Howard" <Ara.T.Howard@noaa.gov> writes:

>the above behaviour of system means the following rules must be followed when
>making system calls:

If you put the shell special characters ("*?{}<>()~&|\\$;'`\"\n") in
the command string, system() will prepend "sh -c" to the command string,
so that

no-such-command

was interpreted as

sh -c no-such-command

that explains the behavior.

              matz.

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

i like this. i use this sort of thing alot

   ret = method

   case ret
     when true
     when false
     when nil
   end

but some might not like it. i think it's clean since it still allows

   system cmd or abort "failed on <#{ cmd }>"

i just have a feeling that combination of exception and exitstatus checking
will suprise many. although i admit the separation dose make logical sense
to me now...

-a

···

On Wed, 30 Jun 2004 nobu.nokada@softhome.net wrote:

Kernel#system returns true when the command succeeded, or false.

One thought:
system could return nil keeping error info when the command
couldn't execute, as perl does. Then, the current behavior
would moved to Process.start, invoke or something else.

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Hi,

At Wed, 30 Jun 2004 11:22:54 +0900,
Ara.T.Howard wrote in [ruby-talk:104891]:

> One thought:
> system could return nil keeping error info when the command
> couldn't execute, as perl does. Then, the current behavior
> would moved to Process.start, invoke or something else.

i like this. i use this sort of thing alot

I noticed that I've forgotten to send this patch. This
separates Kernel#system and Process#system, the former behaves
as older version and the latter does as current version. The
name is provisional.

Index: process.c

···

===================================================================
RCS file: /cvs/ruby/src/ruby/process.c,v
retrieving revision 1.107
diff -U2 -p -d -r1.107 process.c
--- process.c 29 Jun 2004 01:17:39 -0000 1.107
+++ process.c 1 Jul 2004 02:04:36 -0000
@@ -1575,5 +1575,5 @@ rb_spawn(argc, argv)
/*
  * call-seq:
- * system(cmd [, arg, ...]) => true or false
+ * Process.system(cmd [, arg, ...]) => true or false
  *
  * Executes _cmd_ in a subshell, returning +true+ if the command ran
@@ -1592,5 +1592,5 @@ rb_spawn(argc, argv)

static VALUE
-rb_f_system(argc, argv)
+rb_proc_system(argc, argv)
     int argc;
     VALUE *argv;
@@ -1610,4 +1610,37 @@ rb_f_system(argc, argv)
/*
  * call-seq:
+ * system(cmd [, arg, ...]) => true, false or nil
+ *
+ * Same as <code>Process.system</code> except for no exception raise
+ * but nil is returned.
+ */
+
+static VALUE
+rb_f_system(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int status;
+
+ status = rb_spawn(argc, argv);
+ if (status == -1) {
+ VALUE args[2], e;
+ args[0] = argv[0];
+ args[1] = INT2FIX(errno);
+ e = rb_class_new_instance(2, args, rb_eSystemCallError);
+ rb_warning("failed to execute: %s", RSTRING(rb_obj_as_string(e))->ptr);
+ ruby_errinfo = e;
+ return Qnil;
+ }
+#if defined(HAVE_FORK) || defined(HAVE_SPAWNV)
+ rb_syswait(status);
+ status = NUM2INT(rb_last_status);
+#endif
+ if (status == EXIT_SUCCESS) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
  * spawn(cmd [, arg, ...]) => pid
  *
@@ -3472,4 +3505,5 @@ Init_process()

     rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
+ rb_define_singleton_method(rb_mProcess, "system", rb_proc_system, -1);
     rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
     rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);

--
Nobu Nakada