Запуск функции python как команды bash
Существует много литературы о том, как запускать команды оболочки из python, но мне интересно делать противоположное. У меня есть python-модуль mycommands.py, который содержит такие функции, как ниже
def command(arg1, arg2):
pass
def command1(arg1, arg2, arg3):
pass
где аргументы функции - все строки. Цель состоит в том, чтобы иметь возможность запускать эти функции из bash, как показано ниже
$ command arg1 arg2
$ command1 arg1 arg2 arg3
До сих пор у меня есть следующая грубая настройка в .bash_profile, где я должен обеспечить привязку bash к каждой функции python вручную
function command() {
python -c "import mycommand as m; out=m.command('$1', '$2'); print(out)"
}
function command1() {
python -c "import mycommand as m; out=m.command1('$1', '$2', '$3'); print(out)"
}
Было бы неплохо, если бы у вас была одна команда bash, например
$ import_python mycommands.py
который автоматически импортирует все функции python в модуле в виде команд bash. Существует ли библиотека, которая реализует такую команду?
Ответы
Ответ 1
Вы можете создать базу script, скажем command.py
и проверить, с каким именем было вызвано это имя script (не забудьте сделать его исполняемым):
#!/usr/bin/python
import os.path
import sys
def command1(*args):
print 'Command1'
print args
def command2(*args):
print 'Command2'
print args
commands = {
'command1': command1,
'command2': command2
}
if __name__ == '__main__':
command = os.path.basename(sys.argv[0])
if command in commands:
commands[command](*sys.argv[1:])
Затем вы можете создать мягкие ссылки на этот script:
ln -s command.py command1
ln -s command.py command2
и, наконец, протестируйте его:
$ ./command1 hello
Command1
('hello',)
$ ./command2 world
Command2
('world',)
Ответ 2
В зависимости от вашего фактического варианта использования лучшим решением может быть просто Click (или, по крайней мере, модуль argparse
Стандартная библиотека), чтобы создать свой Python script и называть его
command sub-command arg1 args2
из оболочки.
См. Mercurial для одного из ярких примеров этого.
Если вы действительно должны иметь свои команды в качестве команд оболочки первого уровня, используйте символические ссылки или псевдонимы, как описано в других ответах.
С помощью всего нескольких методов вы можете символически ссылаться и отправлять на sys.argv[0]
:
$ cat cmd.py
#!/usr/bin/env python
if __name__ == '__main__':
import sys
from os.path import basename
cmd = basename(sys.argv[0])
print("This script was called as " + cmd)
$ ln -s cmd.py bar
$ ln -s cmd.py foo
$ ./foo
This script was called as foo
$ ./bar
This script was called as bar
С более чем несколькими подкомандами вы можете добавить к вашему Python script что-то вроде следующего: "автоматизировать" процесс:
#!/usr/bin/env python
import sys
def do_repeat(a, b):
print(a*int(b))
def do_uppercase(args):
print(''.join(args).upper())
def do_eval():
print("alias repeat='python cmd.py repeat';")
print("alias uppercase='python cmd.py uppercase';")
if __name__ == '__main__':
cmd = sys.argv[1]
if cmd=='--eval':
do_eval()
else:
args = sys.argv[2:]
if cmd=='repeat':
do_repeat(*args)
elif cmd=='uppercase':
do_uppercase(args)
else:
print('Unknown command: ' + cmd)
Вы бы использовали его как это (предполагая, что исполняемый Python script называется cmd.py
где-то в вашем $PATH
):
$ cmd.py --eval # just to show what the output looks like
alias repeat='python cmd.py repeat';
alias uppercase='python cmd.py uppercase';
$ eval $(python cmd.py --eval) # this would go in your .bashrc
$ repeat asdf 3
asdfasdfasdf
$ uppercase qwer
QWER
NB: Вышеприведенный пример является очень рудиментарным примером того, как использовать eval
в оболочке.
Ответ 3
Вы можете запустить script с аргументами, а затем сделать что-то вроде этого:
(f1 вызывается без аргументов, f2 с двумя аргументами. Обработка ошибок еще не доказана.)
import sys
def run():
if len(sys.argv)<2:
error()
return
if sys.argv[1] == 'f1':
f1()
elif sys.argv[1] == 'f2':
if(len(sys.argv)!=4):
error()
return
f2(sys.argv[2],sys.argv[3])
else:
error()
def f1():
print("f1")
def f2(a,b):
print("f2, arguments:",a,b)
def error():
print("error")
run()
Это не позволяет вам вызывать функции типа
commmand arg1 arg2
но вы можете сделать
python test.py f1
python test.py f2 arg1 arg2
Edit:
Вы можете создавать псевдонимы:
alias f1="python test.py f1"
alias f1="python test.py f2"
чтобы достичь того, что вы запросили:
$ f1
$ f2 arg1 arg2
Ответ 4
Ткань может сделать именно это:
from fabric.api import run
def host_type():
run('uname -s')
fab -H localhost, linuxbox host_type
[localhost] run: uname -s
[localhost]: Дарвин
[linuxbox] run: uname -s
[linuxbox]: Linux
Готово.
Отключение от localhost... done.
Отключение от linuxbox... done.
Или более конкретный вопрос:
$ cat fabfile.py
def command1(arg1, arg2):
print arg1, arg2
$ fab command1:first,second
первая секунда
Готово.
Ответ 5
autocommand кажется уместным рассмотреть (cli непосредственно из сигнатур функций)
Пример:
@autocommand(__name__)
def cat(*files):
for filename in files:
with open(filename) as file:
for line in file:
print(line.rstrip())
$ python cat.py -h
usage: ipython [-h] [file [file ...]]
positional arguments:
file
optional arguments:
-h, --help show this help message and exit