Запуск функции 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