This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
com_kieffer's profile picture

Pass variables to a shell spawned with os.run

Started by com_kieffer, 14 October 2014 - 06:39 PM
com_kieffer #1
Posted 14 October 2014 - 08:39 PM
I'm trying to pass variables to a new shell started with os.run() but it isn't working.

Here's more or less what I want to do :

print("skip_init = " .. tostring(skip_init))
read() -- pause
os.run({["skip_init"] = true}, "/rom/programs/shell")

I would expect the program to print :

skip_init = nil
skip_init = true
skip_init = true
...

Instead it keeps printing "skip_init = nil".
How can I pass the skip_init variable to the shell ?



I need this to make a monitoring system for computers work. Basically at startup I spawn a background task that listens for a specific event ("checkin") and posts the event data to a server. If no events are received within a certain time period it just sends a ping message to let the server know that it is still alive. My startup file looks like this :


if not system_running then
	dofile("/init")

	checkin = require "checkin"
	parallel.waitForAny(
		checkin.daemon,

		function()
			os.run({["require"] = require, ["system_running"] = true}, "/rom/programs/shell")
		end
	)

	print("error")
	read()
	os.reboot()
end

Where the /init file loads (among other things) the require method and system_running is meant to force the shell that gets spawned in the parallel call to skip the startup code.
valithor #2
Posted 15 October 2014 - 03:09 AM
I have never used os.run(), but from reading whahttp://www.comput the wiki says tercraft.info/wiki/Os.run.

It appears that you can pass arguments to it like this, os.run({["skip_init"] = true}, "/rom/programs/shell", 5, 100, 32)
In that example 5, 100, and 32 are passed to the program that you are running as if you were to type "/rom/programs/shell 5 100 32" into the computer.
com_kieffer #3
Posted 15 October 2014 - 11:11 AM
That doesn't solve my problem.

I need to pass the "skip_init" variable so that I can chose the skip the body of the /startup file that gets run when you start a new shell.
MKlegoman357 #4
Posted 15 October 2014 - 02:13 PM
I don't see the reason why it actually fails to you but I see something that I think you are not expecting to happen. When you run the shell it first checks if there is the 'shell' API available to it, so it could know if it is the first instance of the shell. If it doesn't find the 'shell' API then it runs the startup file. To fix this issue you should pass 'shell' (and 'multishell' if running CC 1.6+) API to your new shell:


os.run({ ["skip_init"] = true, ["shell"] = shell, ["multishell"] = multishell }, "/rom/programs/shell")
ElvishJerricco #5
Posted 15 October 2014 - 03:44 PM
You can't tell the shell what to do. The only thing the shell will listen to is its arguments. You can give the shell an environment to play in, but it won't make its programs play in that environment. A program launched by a shell is run with os.run(tEnv, path), where tEnv is a table that the shell created that only has shell and multishell fields. So those programs don't receive the environment variables that the shell has.

I'm not exactly sure what you're trying to accomplish, so I'm not sure my suggestions will be helpful. But if all you need is to skip the startup file, why not do shell.run("shell mainProgram")? Using shell.run to start a shell doesn't run startup again.
com_kieffer #6
Posted 15 October 2014 - 06:49 PM
It looks like my approach is pretty flawed. I'll try to explain what I'm doing more clearly :

In previous iterations of my code I would overwrite the error() function and add a require() function to the environment. This happened in the /startup and everything was fine.

Programs could use

api = require 'api_name'
to get modules and any errors they encountered would be reported to a remote server thanks to our overwritten error function.

One day I decided that I wanted computers to automatically report their status to this server. To achieve this I need as background task that basically sets a timer and sends new data every time the timer event is fired.

If I wasn't trying to add the require and the new error method to the environment on startup I would use a /startup file like this :


parallel.waitForAny(
    my_background_task,
    shell.run("shell")
)

I'm not sure how to get the best of the 2 worlds. How can I keep the background task and still be able to modify the environment that commands will be running in.

The ugly way would simply be to run dofile on a bootstrap file that sets up the environment for me at the start of every program but that seems ugly.
Lyqyd #7
Posted 15 October 2014 - 07:38 PM
Use os.run instead and set up your modified environment, then pass it as the first argument to os.run.
com_kieffer #8
Posted 17 October 2014 - 02:57 PM
Use os.run instead and set up your modified environment, then pass it as the first argument to os.run.

That's exactly what I'm doing. My previous post was just attempting explaining why I need to be able to inject stuff into the shell. The test case I posted in the first example :


print("some_var= " .. tostring(some_var))
read() -- pause
os.run({["some_var"] = true}, "/rom/programs/shell")

is a better representation of what my code looks like. This isn't the actual code but just test case to try and make this work. Instead of printing :

some_var = nil
some_var = true
...
it just prints some_var = nil forever !


You can't tell the shell what to do. The only thing the shell will listen to is its arguments. You can give the shell an environment to play in, but it won't make its programs play in that environment. A program launched by a shell is run with os.run(tEnv, path), where tEnv is a table that the shell created that only has shell and multishell fields. So those programs don't receive the environment variables that the shell has.

This makes me think that the 2 solutions to the problem are :
  1. Use multishell and just start each program in it's own shell
  2. Ship a modified shell that imports the functions I want and start it from the /startup file.
ElvishJerricco #9
Posted 17 October 2014 - 05:35 PM
I think the better solution is to modularize your code. Have a startup file that only ever needs to be run once, then everything else happens in a different file that you call at the appropriate times. This way you don't need this weird variable in weird places.