0

There are many ways in ruby to make a system call.

I'm currently using open3 in a method like this:

def run_system_command { |system_command|
   
  stdout_str = ""
  stderr_str = ""
  status = ""
 
  Open3.popen3(system_command) do |stdin, stdout, stderr, wait_thr|
     stdin.close                
     stdout_str = stdout.read   # read stdout to string. note that this will block until the command is done!
     stderr_str = stderr.read   # read stderr to string
     status = wait_thr.value    # will block until the command finishes; returns status that responds to .success? etc
  end
 
  return stdout_str, stderr_str, status
}

It works fine when running basic system commands like tar or ls or cd etc...

But when I try to run a program like ios-deploy from an Automator 'Run Shell Script', I don't get any results:

enter image description here

(Assume the run_system_command method is defined in the block. Automator text fields don't expand so I couldn't show you the whole thing)

I get no output from such a call, which makes me think that making a system call in Ruby doesn't see the ios-deploy program.

Note that I've made sure that I can get output from Ruby in general by using 'Set Value of Variable' and 'Display Notification' actions in a simple "Hello World" 'Run Shell Script' action.

How can I tell ruby how to use / where to find the program?

Some kind of path adjustment? I'm not sure where to make such a change when calling Ruby in this way.

Update: I'm able to get the command to work by using the full path to the program:

/usr/local/bin/ios-deploy

Is there a way to tell Ruby/Automator where to find that program without having to explicitly use the path prefix every time?

kraftydevil
  • 5,144
  • 6
  • 43
  • 65
  • 1
    BTW, calling `define_method` is rather unusual. You would typically define a method via `def`, see the docs for [Methods](https://ruby-doc.org/3.2.2/syntax/methods_rdoc.html) – Stefan Jul 13 '23 at 09:50
  • I defined this method with `define_method` so it could see file level / global variables below the require statements. Basically my copy of the method also has an `if verbose_mode_on` statement that prints all 3 fields. I didn't include it in the question so as not to distract from my issue. It would not be good style for a larger project but works for my purposes – kraftydevil Jul 13 '23 at 14:16
  • 1
    For portability, the `Run Shell Script` action does not read the configuration files that an interactive shell running in Terminal would. In particular, $PATH is set to a default value and will not have any of your customizations, so you will need to use the full path or set up the environment in the shell. – red_menace Jul 13 '23 at 16:21
  • You need to show your Automator code, too. `return stdout_str, stderr_str, status` will return an Array (if it returns anything), so we have no idea what your Apple Script or Automator code is doing with this value. You might also consider just sheling out from inside `%x()` and see what values actually are available on _STDOUT_ from Ruby's POV. – Todd A. Jacobs Jul 13 '23 at 17:25
  • You also don't show where you're calling `require "open3"` in Ruby. It's part of the standard library, not core, so it's not loaded automatically. I'm not sure why you're not getting an exception for that, unless you're not giving us a proper [MCVE](https://stackoverflow.com/help/minimal-reproducible-example). – Todd A. Jacobs Jul 13 '23 at 17:31
  • As mentioned, "Automator text fields don't expand so I couldn't show you the whole thing". Please assume all of the typical logistics are taken care of. I also mentioned that "I've made sure that I can get output from Ruby in general" so that's been eliminated as an issue. I know the command isn't running properly because when I run it with other options that should install the app on devices - it doesn't do it. Running the same command in Terminal produces a successful install – kraftydevil Jul 14 '23 at 02:44

1 Answers1

1

Is there a way to tell Ruby/Automator where to find that program without having to explicitly use the path prefix every time?

Kind of. Automator runs Ruby (as well as the other shell scripts) using an environment with PATH set to /usr/bin:/bin:/usr/sbin:/sbin, i.e. it will only search for executables in /usr/bin, /bin, /usr/sbin and /sbin.

I don't see any way to configure the environment in Automator, but at least you can modify the environment in Ruby via ENV#[]=, e.g.:

ENV['PATH'] = "/usr/local/bin:#{ENV['PATH']}"

This way, executables in /usr/local/bin will become available when running a shell command:

"Run Shell Script" action in Automator

Open3.popen3 also accepts environment variables as an argument: (see the docs for Process.spawn)

require 'open3'

env = { 'PATH' => "/usr/local/bin:#{ENV['PATH']}" }
command = 'brew --version'

Open3.popen3(env, command) do |stdin, stdout, stderr, wait_thr|
  # ...
end
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • I appreciate this, but the output is not my issue. I only mentioned `ios-deploy -c` not producing output as a symptom proving that the system call to `ios-deploy -c` never returns. I am interested in how I can tell ruby where `ios-deploy` is - at least for the running of the script. Right now I have modified it to `run_system_command("/usr/local/bin/ios-deploy -c")` and that works for my purposes when using with `puts stdout_str`. At the same time, I would prefer not to use the full path to `ios-deploy` every time. I will update the question to better communicate this. – kraftydevil Jul 13 '23 at 15:14
  • @kraftydevil I've updated my answer accordingly – Stefan Jul 14 '23 at 07:40