可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am almost brand new to python scripting, so please excuse any stupid questions, but any help anyone can give would be much appreciated.
I am trying to write a python script for other people to use, and in it I need to call a program that I won't always know the path to. To get around that, I ask the user to provide the path to the program, which will work, but I don't want users to have to provide the path EVERY time they run the script so I have been trying to set up a bash alias by having the script add to the ~/.profile and ~/.bashrc files.
I can then use the alias to run the program from an interactive bash shell, but when the script tries to run it, I get a "command not found" error...
I have tried re-sourcing the .bashrc file and using the "shopt -s expand_aliases" command with no luck.
My ~/.bashrc looks like this:
alias nuke='/Applications/Nuke6.2v4/Nuke6.2v4.app/Contents/MacOS/Nuke6.2v4'
And the piece of the script looks like this:
os.system('source .bashrc')
os.system('shopt -s expand_aliases')
os.system('nuke -x scriptPath')
But once the script gets up to this point, it returns:
sh: nuke: command not found
Am I doing something wrong or is there another way I can permanently store the path to a program?
回答1:
The module you want is subprocess.
A quick fix to your problem is to use the subprocess module, like so:
import subprocess
sp = subprocess.Popen(["/bin/bash", "-i", "-c", "nuke -x scriptpath"])
sp.communicate()
This is equivalent to calling:
nuke -x scriptpath
from the bash shell. The -i flag tells bash to behave as though it's an interactive session (and use the ~/.bashrc file)
BUT, you should be really really careful you're not opening yourself up to any shell injection (for instance, if this command is called from a CGI page)
For quick scipts that users invoke directly from the shell they probably can't do any more damage than they could with general shell access, but if this script is called by a web page a malicious user could pass something a bit like "rm -dfr ~/ &" as the program.*
If the number of executables is small, you might be better off either naming them in the script:
PROGRAMS = {"nuke": "/path/to/nuke"
"foo" : "/path/to/foo" }
# Parse command line args
program = sys.argv[1]
sp = subprocess.Popen([PROGRAMS[program], "other", "arguments", "to", "program"])
*This might not work exactly like this, but you get the idea
回答2:
Be aware that os.system
is probably using sh
rather than bash
, and so source
and shopt
will fail as well.
If it is using bash
, it will fail as os.system
creates a new process for each call. You could do it in one line like this:
os.system('source .bashrc; shopt -s expand_aliases; nuke -x scriptPath')
But you are by far best to get the path in some other way (or even read it from .bashrc
manually if you want) and then use subprocess.Popen()
.
回答3:
I know this is an old question, but for anyone else who comes across this in the future, I think it's worth mentioning that modifying someone's ~/.bashrc or ~/.profile files (especially silently) is one of those ideas that generally falls under the umbrella of "bad practice". Additionally, it seems a bit heavy-handed for the problem you need to solve.
Instead, why not have your script keep track of its own configuration file stored in the user's home directory? It would be quite simple to do this using ConfigParser
, your own JSON structure dumped to a file, or something else entirely if you want.
Then in your script, you can first check if it exists, and if it does, see if it contains the key you're looking for that holds the executable path. If either of those tests fail, you know you need to prompt the user for the path, at which point you can write it to the config file for next time.
回答4:
Yeah don't do that. Write your configuration out into your own dotfile, and don't use os.system
, use subprocess
.
回答5:
I think an alias is a very complicated and not greatly intuitive way of using the shell environment. How about using environment variables instead? That's basically what they're for...
Instead of asking your users to define an alias nuke
, ask them to define an environment variable $NUKE
. This save you from messing with .bashrc
or any other configuration file. If the user adds export NUKE=<path>
to their .bashrc
it is automatically available in the environment when executing the python script interactively.
If you only need this path to make a system call, just use os.system('$NUKE -x scriptPath')
.
If you need the value in python, it is easy to access as well: after import os
, os.environ
gives you a dictionary of all environment variables currently defined. Getting the value an alias is set to on the contrary is very cumbersome in python.