If you want your script to return values, just do return [1,2,3] from a function wrapping your code but then you'd have to import your script from another script to even have any use for that information:
Return values (from a wrapping-function)
(again, this would have to be run by a separate Python script and be imported in order to even do any good):
import ...
def main():
# calculate stuff
return [1,2,3]
Exit codes as indicators
(This is generally just good for when you want to indicate to a governor what went wrong or simply the number of bugs/rows counted or w/e. Normally 0 is a good exit and >=1 is a bad exit but you could inter-prate them in any way you want to get data out of it)
import sys
# calculate and stuff
sys.exit(100)
And exit with a specific exit code depending on what you want that to tell your governor. I used exit codes when running script by a scheduling and monitoring environment to indicate what has happened.
(os._exit(100) also works, and is a bit more forceful)
Stdout as your relay
If not you'd have to use stdout to communicate with the outside world (like you've described). But that's generally a bad idea unless it's a parser executing your script and can catch whatever it is you're reporting to.
import sys
# calculate stuff
sys.stdout.write('Bugs: 5|Other: 10\n')
sys.stdout.flush()
sys.exit(0)
Are you running your script in a controlled scheduling environment then exit codes are the best way to go.
Files as conveyors
There's also the option to simply write information to a file, and store the result there.
# calculate
with open('finish.txt', 'wb') as fh:
fh.write(str(5)+'\n')
And pick up the value/result from there. You could even do it in a CSV format for others to read simplistically.
Sockets as conveyors
If none of the above work, you can also use network sockets locally *(unix sockets is a great way on nix systems). These are a bit more intricate and deserve their own post/answer. But editing to add it here as it's a good option to communicate between processes. Especially if they should run multiple tasks and return values.
Answer from Torxed on Stack OverflowWithin sh which is actually dash on Ubuntu the builtin command return can returns only numerical values - exit statuses, which have a meaning in a context of a function or sourced script. Source man sh:
The syntax of the return command is
return [exitstatus]
Everything other with your shell script looks correct. I think you need to use echo $COLOR instead return and suppress other echo-es.
In case you need to return more data to the main script you can output everything as one line and divide the separate fields by some character that will play role of a delimiter in the main script on which base you can convert the string into an array. For example (where , is our delimiter and -n will suppers the newline character within echo):
echo -n "$COLOR","$exitstatus"
The other information that is provided by the script and is not required by the main script could be redirected to some log file:
$ cat whiptail.sh
#!/bin/sh
log_file='/tmp/my.log'
COLOR=$(whiptail --inputbox "What is your favorite Color?" 8 78 Blue --title "Example Dialog" 3>&1 1>&2 2>&3)
exitstatus=
exitstatus = 0 ]; then
echo "User selected Ok and entered $COLOR" > "$log_file"
echo -n "$COLOR","$exitstatus"
else
echo "User selected Cancel." >> "$log_file"
echo -n "CANCEL","$exitstatus"
fi
Unfortunately I don't have much experience with Python, but here is a sample .py script that can handle the output of the above .sh script (reference):
$ cat main-script.py
#!/usr/bin/python
import subprocess
p = subprocess.Popen(['./whiptail.sh'], stdout=subprocess.PIPE)
p = p.communicate()[0]
p = p.split(",")
print "Color: " + p[0]
print "ExitCode: " + p[1]
I had a similar problem where I needed the return value from a shell command in my Python script.
The subprocess method check_output() helped me get the shell return code as a byte string in the Python file.
Here is the code:
return_value =subprocess.check_output("sudo raspi-config nonint get_spi", stderr=subprocess.STDOUT, shell = True)
print(return_value)
<<<b'1\n'
You can't return message as exit code, only numbers. In bash it can accessible via $?. Also you can use sys.argv to access code parameters:
import sys
if sys.argv[1]=='hi':
print 'Salaam'
sys.exit(0)
in shell:
#!/bin/bash
# script for tesing
clear
echo "............script started............"
sleep 1
result=`python python/pythonScript1.py "hi"`
if [ "$result" == "Salaam" ]; then
echo "script return correct response"
fi
Pass command line arguments to shell script to Python like this:
python script.py
2 $3
Print the return code like this:
echo $?
sys.exit(myString) doesn't mean "return this string". If you pass a string to sys.exit, sys.exit will consider that string to be an error message, and it will write that string to stderr. The closest concept to a return value for an entire program is its exit status, which must be an integer.
If you want to capture output written to stderr, you can do something like
python yourscript 2> return_file
You could do something like that in your bash script
output=$((your command here) 2> &1)
This is not guaranteed to capture only the value passed to sys.exit, though. Anything else written to stderr will also be captured, which might include logging output or stack traces.
example:
test.py
print "something"
exit('ohoh')
t.sh
va=$(python test.py 2>&1)
mkdir $va
bash t.sh
edit
Not sure why but in that case, I would write a main script and two other scripts... Mixing python and bash is pointless unless you really need to.
import script1
import script2
if __name__ == '__main__':
filename = script1.run(sys.args)
script2.run(filename)
sys.exit() should return an integer, not a string:
sys.exit(1)
The value 1 is in $?.
$ cat e.py
import sys
sys.exit(1)
$ python e.py
$ echo $?
1
Edit:
If you want to write to stderr, use sys.stderr.
Ok, if I understand you correctly you want to:
- pass an argument to another script
- retrieve an output from another script to original caller
I'll recommend using subprocess module. Easiest way would be to use check_output() function.
Run command with arguments and return its output as a byte string.
Sample solution:
script1.py
import sys
import subprocess
s2_out = subprocess.check_output([sys.executable, "script2.py", "34"])
print s2_out
script2.py:
import sys
def main(arg):
print("Hello World"+arg)
if __name__ == "__main__":
main(sys.argv[1])
The recommended way to return a value from one python "script" to another is to import the script as a Python module and call the functions directly:
import another_module
value = another_module.get_value(34)
where another_module.py is:
#!/usr/bin/env python
def get_value(*args):
return "Hello World " + ":".join(map(str, args))
def main(argv):
print(get_value(*argv[1:]))
if __name__ == "__main__":
import sys
main(sys.argv)
You could both import another_module and run it as a script from the command-line. If you don't need to run it as a command-line script then you could remove main() function and if __name__ == "__main__" block.
See also, Call python script with input with in a python script using subprocess.
Apache creates its own users to run its jobs. When run as a php command, you are almost surely running as some such Apache-created user that has different permissions than your user account. Accessing the GPIO requires special permission, which the php user probably doesn't have. That will cause it to fail when called, which is exactly what you are seeing.
To further debug this, you should check the error logs created when the job fails and possibly identify the user that is executing that job. These links will help:
- https://stackoverflow.com/questions/7771586/how-to-check-what-user-php-is-running-as
- https://stackoverflow.com/questions/5127838/where-does-php-store-the-error-log-php5-apache-fastcgi-cpanel
Summarizing the key point about the error logging, the error usually appears under /var/log/apache2 on the Pi. (You'll see other potential locations listed for other hardware and software options at the stack overflow questions since they are more general.)
To address the underlying permissions error, if that's what it ultimately is, you may have a couple of options. You might be able to grant the appropriate permissions to the php user, or your might run it with sudo from your php script. That, in turn, may require you to add the php use to the list of sudoers. Be careful with either option - especially if there's public access - because you'll be allowing external control of the pins on your Pi!
Having mentioned how to do this, I think it's worth noting that this probably indicative of a bad design anyway. Based on what you said, it seems like php is the wrong tool for this job. If you must allow changes in state to your Pi via web, you're probably better using cgi-bin programs instead. Usually php is for serving dynamic content in the webpage, not for changing the state of the server. (In this case, the Pi is the server.) That's a whole different set of questions and issues though.
It's almost certainly a permissions issue. Ordinarily, you'd just add www-data to the gpio group (usermod -aG gpio www-data), but due to the fact you're accessing the SPI, it's a bit more complex than usual. You have two choices
- Run apache as
root(not recommended) - Run your script as a background process
Option 1 would quickly tell you if it's a permissions issue, and can be done by changing the user (you'll probably find it in /etc/apache2/envvars).
Option 2 is really a better approach, but is more work. What you need to think about is a long-lived process running in the background, and a way to interact with this from the web thread. This can be done by http/queue/file/fifo/socket among others. I'd recommend having a look at some of the react libraries, or ZeroMQ
With all that in mind, using the PHPi library, you can do this completely in PHP. Here's an example based off one of the examples in the library for interacting with that ADC.
include 'vendor/autoload.php';
use Calcinai\PHPi\Peripheral\SPI;
use Calcinai\PHPi\Pin\PinFunction;
use Calcinai\PHPi\External\ADC\Microchip\MCP3008;
$board = \Calcinai\PHPi\Factory::create();
//Flip the appropriate pins to their alt functions
$board->getPin(10)->setFunction(PinFunction::SPI0_MOSI);
$board->getPin(9)->setFunction(PinFunction::SPI0_MISO);
$board->getPin(11)->setFunction(PinFunction::SPI0_SCLK);
$board->getPin(8)->setFunction(PinFunction::SPI0_CE0_N);
$adc = new MCP3008($board->getSPI(SPI::SPI0), 0);
$pin = $board->getPin(18);
//In 10-bit, will be between 0 and 1024
$value = $adc->getChannel(0)->read();
if($value > 500){
$pin->high();
} else {
$pin->low();
}
Usually you'd be running this as a process, rather than directly via apache.
If the return value (which is unclear in the question) is a single line of output you could simply:
./script.py | tail -n 1
This will output the last line generated from the script (adjust the number as needed).
If you want to save to a file either > to create a new file, or >> to append to a file:
./script.py | tail -n 1 > file
Instead of printing, use the logging module. It will print to stderr by default and you can redirect that somewhere else... OR disable all debugging output. Take a look at the table at the top of this page for usage advice: https://docs.python.org/2/howto/logging.html