Запуск команд Bash в Python


299

На своїй локальній машині я запускаю скрипт python, який містить цей рядок

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)

Це чудово працює.

Потім я запускаю той самий код на сервері і отримую таке повідомлення про помилку

'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import  diag
ImportError: No module named swap

Тож, що я зробив тоді, я вставив a, print bashCommandякий друкує мене, ніж команду в терміналі, перш ніж він запускає його os.system().

Звичайно, я знову отримую помилку (викликану os.system(bashCommand)), але перед цією помилкою вона друкує команду в терміналі. Потім я просто скопіював цей висновок і зробив копію в термінал і натисніть Enter, і це працює ...

Хтось має підказку, що відбувається?


2
Здається, що в середовищі є різниця в залежності від того, як ти працюєш cwm. Можливо, у вас є якась конфігурація, .bashrcяка створює середовище для інтерактивного використання bash?
Sven Marnach

Ви намагалися запустити команду з командного рядка під час входу на сервер? У вашому дописі просто сказано, що ви "вставили [його] в термінал".
Sven Marnach

@Sven: так, я мав на увазі, що я запустив команду безпосередньо в терміналі сервера
mkn

Здається, є різниця в PYTHONPATH залежно від способу бігу cwm. А може, є різниця в PATH, і cwmназиваються різні версії . Або різні версії Python. Справді важко це зрозуміти без доступу до машини ...
Свен Марнах

Відповіді:


314

Не використовуйте os.system. Він застарілий на користь підпроцесу . З документації : «Цей модуль має намір замінити кілька старих модулів і функцій: os.system, os.spawn».

Як у вашому випадку:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

8
Це не зробило те, що я хотів, коли мені потрібно було виконати cd 'path\to\somewhere'іншу команду bash, яку потрібно було запустити кудись. @ user225312
AWrightIV

36
@AWrightIV Якщо вам потрібно, щоб ваш підпроцес був запущений у певному робочому каталозі, ви можете скористатися cwdаргументом для Popen:subprocess.Popen(..., cwd='path\to\somewhere')
водонепроникний

7
Для моєї команди мені знадобилася оболонка = True, як тут; stackoverflow.com/questions/18962785 / ...
user984003

4
У цьому випадку краще використовувати shlex.split (), а не string.split ()
Олексій Свиридов

4
... ( stdout=fileпереспрямовує вихід у файл у цьому випадку. Він реалізує > file). Було б неправильно передавати ..., '>', 'file']останню команду, що очікує перенаправлення (вона не працюватиме без оболонки, і якщо ви використовуєте оболонку, вам слід передати команду як рядок)
jfs

186

Щоб дещо розширити попередні відповіді тут, є ряд деталей, які зазвичай не помічаються.

  • Віддаю перевагу subprocess.run()більш subprocess.check_call()і друзями в протягом subprocess.call()більш ніж subprocess.Popen()над os.system()більшos.popen()
  • Розуміти і, ймовірно, використовувати text=True, ака universal_newlines=True.
  • Зрозумійте значення shell=Trueабо shell=Falseі як це змінює котирування та наявність зручностей оболонки.
  • Зрозумійте відмінності між shБашем
  • Зрозумійте, як підпроцес відокремлений від його батьківського і, як правило, не може змінити батьківський.
  • Уникайте запуску інтерпретатора Python як підпроцесу Python.

Ці теми висвітлюються детальніше нижче.

Віддавайте перевагу subprocess.run()абоsubprocess.check_call()

The subprocess.Popen() функція - це робочий коник низького рівня, але складно використовувати правильно, і ви закінчуєте копіювати / вставляти кілька рядків коду ... які зручно вже існувати у стандартній бібліотеці як набір обгорткових функцій вищого рівня для різних цілей, які більш детально представлені нижче.

Ось абзац з документації :

Рекомендований підхід до виклику підпроцесів полягає у використанні run()функції для всіх випадків використання, з якими вона може працювати. Для більш розширених випадків використання базовий Popenінтерфейс можна використовувати безпосередньо.

На жаль, доступність цих функцій обгортки відрізняється між версіями Python.

  • subprocess.run()був офіційно представлений в Python 3.5. Мається на увазі замінити все наступне.
  • subprocess.check_output()було введено в Python 2.7 / 3.1. Це в основному еквівалентноsubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call()була представлена ​​в Python 2.5. Це в основному еквівалентноsubprocess.run(..., check=True)
  • subprocess.call()була введена в Python 2.4 в оригінальному subprocessмодулі ( PEP-324 ). Це в основному еквівалентноsubprocess.run(...).returncode

API високого рівня проти subprocess.Popen()

Відремонтоване та розширене subprocess.run()є більш логічним та універсальнішим, ніж старі застарілі функції, які він замінює. Він повертає CompletedProcessоб'єкт, який має різні методи, які дозволяють отримати статус виходу, стандартний висновок та кілька інших результатів та індикаторів стану з готового підпроцесу.

subprocess.run()це шлях, якщо вам просто потрібна програма для запуску та повернення контролю до Python. Для більш задіяних сценаріїв (фонові процеси, можливо, інтерактивні введення / виведення з батьківською програмою Python) вам все одно потрібно використовувати subprocess.Popen()та доглядати за всіма сантехніками. Це вимагає досить складного розуміння всіх рухомих частин і не слід сприймати їх з легкістю. Простіший Popenоб'єкт являє собою (можливо, все ще запущений) процес, яким потрібно керувати з вашого коду протягом останнього періоду життя підпроцесу.

Можливо, слід підкреслити, що просто subprocess.Popen()лише створюється процес. Якщо ви залишите це на цьому, у вас є підпроцес, який працює одночасно з Python, так що "фоновий" процес. Якщо вам не потрібно робити введення або вихід або координувати інший спосіб з вами, він може робити корисну роботу паралельно з вашою програмою Python.

Уникайте os.system()іos.popen()

З часу вічної (ну, оскільки Python 2.5) osдокументація модуля містила рекомендацію віддавати перевагу subprocessнад os.system():

subprocessМодуль надає більш потужні засоби для породження нових процесів і отримання їх результатів; використання цього модуля переважно, ніж використання цієї функції.

Проблеми system()полягають у тому, що він, очевидно, залежить від системи і не пропонує способів взаємодії з підпроцесом. Він просто працює, із стандартним виходом і стандартною помилкою поза межами досяжності Python. Єдина інформація, яку Python отримує назад, - це стан виходу команди (нуль означає успіх, хоча значення ненульових значень також дещо залежать від системи).

PEP-324 (про який вже згадувалося вище) містить більш детальне обґрунтування того, чому os.systemце проблематично та як subprocessнамагаються вирішити ці питання.

os.popen()звикли ще сильніше зневажати :

Застаріло з версії 2.6: Ця функція застаріла. Використовуйте subprocessмодуль.

Однак, оскільки колись у Python 3 він був доповнений для простого використання subprocessта перенаправлення до subprocess.Popen()документації для отримання деталей.

Зрозумійте і зазвичай використовуйте check=True

Ви також помітите, що subprocess.call()має багато тих же обмежень, що і os.system(). При регулярному використанні слід загалом перевірити, чи успішно завершився процес, що subprocess.check_call()і subprocess.check_output()робити (де останній також повертає стандартний вихід готового підпроцесу). Крім того , ви повинні зазвичай використовувати check=Trueз , subprocess.run()якщо ви спеціально не потрібно , щоб подпроцесс повернути стан помилки.

На практиці, з check=Trueабо subprocess.check_*, Python викине CalledProcessErrorвиняток, якщо підпроцес поверне статус ненульового виходу.

Поширена помилка з subprocess.run()- це опустити check=Trueта здивуватися, коли вихідний код вийде з ладу, якщо підпроцес не вдався.

З іншого боку, поширеною проблемою було check_call()і check_output()те, що користувачі, які сліпо користувалися цими функціями, були здивовані, коли виняток було порушено, наприклад, коли grepне знайшли відповідності. (Ви, ймовірно, grepвсе одно повинні замінити нативним кодом Python, як зазначено нижче.)

Враховуючи все, вам потрібно зрозуміти, як команди оболонки повертають код виходу, і за яких умов вони повернуть ненульовий (помилку) код виходу, і прийняти свідоме рішення про те, як саме слід обробляти.

Зрозумійте і, ймовірно, використовуйте text=Trueакаuniversal_newlines=True

Оскільки Python 3, рядки, внутрішні в Python, є рядками Unicode. Але немає гарантії, що підпроцес генерує вихід Unicode або взагалі рядки.

(Якщо відмінності не відразу очевидні, Прагматичний Unicode Неда Батчелдера рекомендується читати, якщо не прямо, обов'язково, читати. За посиланням стоїть 36-хвилинне відеопрезентація, хоч читання сторінки самостійно, ймовірно, займе значно менше часу. )

Глибоко вниз, Python повинен отримати bytesбуфер і якось його інтерпретувати. Якщо він містить фрагмент бінарних даних, його не слід розшифровувати у рядок Unicode, оскільки це схильна до помилок та поведінка викликає помилку - саме таку прискіпливу поведінку, яка позбавила багатьох сценаріїв Python 2, перш ніж з'явився спосіб правильно розрізняти закодований текст і бінарні дані.

З text=True, ви говорите Python, що ви насправді очікуєте повернення текстових даних у кодування за замовчуванням системи, і що вони повинні бути декодовані в рядок Python (Unicode) якнайкраще з можливостей Python (як правило, UTF-8 на будь-яких помірно до система дат, крім можливо Windows?)

Якщо це не те, що ви запитуєте назад, Python просто надасть вам bytesрядки в рядках stdoutі stderrрядках. Може бути , в якій - то пізніше ви ж знаєте , що вони були текстові рядки в кінці кінців, і ви знаєте , їх кодування. Потім ви можете їх розшифрувати.

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

Python 3.7 представив коротший і більш описовий і зрозумілий псевдонім textдля аргументу ключового слова, який раніше був дещо оманливим universal_newlines.

Зрозумійте shell=Trueпротиshell=False

Коли shell=Trueви передаєте одну струну до своєї оболонки, і оболонка бере її звідти.

З shell=Falseпередачею списку аргументів в ОС, минаючи оболонку.

Якщо у вас немає оболонки, ви зберігаєте процес і позбавляєтесь від значної кількості прихованої складності, яка може або не може породжувати помилки або навіть проблеми із безпекою.

З іншого боку, якщо у вас немає оболонки, у вас немає перенаправлення, розширення підстановки, контролю роботи та великої кількості інших функцій оболонки.

Поширена помилка - використовувати, shell=Trueа потім все-таки передавати Python списку жетонів, або навпаки. Це трапляється в деяких випадках, але це дійсно неправильно визначено і може зламатись цікавими способами.

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

Загальна реторта "але це працює для мене" не є корисним спростуванням, якщо ви точно не розумієте, за яких обставин воно може перестати працювати.

Приклад рефакторингу

Дуже часто функції оболонки можна замінити нативним кодом Python. Простий Awk абоsed сценарії, ймовірно, слід просто перевести замість цього на Python.

Щоб частково проілюструвати це, ось типовий, але трохи нерозумний приклад, який включає багато особливостей оболонки.

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Тут слід зазначити деякі речі:

  • З shell=Falseвами не потрібно цитування, необхідного для оболонки навколо рядків. Як правило, розміщення лапок - це, мабуть, помилка.
  • Часто має сенс запустити якомога менше коду в підпроцесі. Це дає вам більше контролю над виконанням всередині вашого Python-коду.
  • Сказавши це, складні трубопроводи з оболонками є втомливими і іноді складними для повторного втілення в Python.

Реконструйований код також ілюструє те, наскільки насправді оболонка робить для вас дуже ситний синтаксис - на краще чи на гірше. Python каже, що явний кращий, ніж неявний, але код Python є досить багатослівним і, мабуть, виглядає складніше, ніж це насправді. З іншого боку, він пропонує ряд точок, де ви можете захопити контроль посеред чогось іншого, як це банально пояснюється вдосконаленням, що ми можемо легко включити ім'я хоста разом із висновком команди оболонки. (Це ні в якому разі не складно зробити в оболонці, але за рахунок ще однієї диверсії та, можливо, іншого процесу.)

Загальні конструкції оболонок

Для повноти, ось короткі пояснення деяких цих особливостей оболонки, а також деякі примітки про те, як їх можливо замінити на рідні засоби Python.

  • Розширення підстановки під назвою Globbing можна замінити glob.glob()або дуже часто простими порівняннями рядків Python for file in os.listdir('.'): if not file.endswith('.png'): continue. Bash має різноманітні інші засоби розширення, такі як .{png,jpg}розширення брекетів {1..100}, а також розширення нахилу ( ~розширюється до домашнього каталогу та загалом ~accountдо домашнього каталогу іншого користувача)
  • Змінні оболонки на кшталт $SHELLабо $my_exported_varіноді просто можуть бути замінені змінними Python. Експортовані змінні оболонки доступні як наприклад os.environ['SHELL'](сенс exportполягає в тому, щоб зробити змінну доступною для підпроцесів - змінна, яка недоступна для підпроцесів, очевидно, буде недоступною для Python, що працює як підпроцес оболонки, або навпаки. env=Ключове слово Аргумент subprocessметодам дозволяє визначити середовище підпроцесу як словник, тому це один із способів зробити змінну Python видимою для підпроцесу). З shell=Falseвами потрібно буде зрозуміти, як видалити будь-які лапки; наприклад, cd "$HOME"еквівалент os.chdir(os.environ['HOME'])без лапок навколо імені каталогу. (Дуже частоcdтак чи інакше не є корисним або необхідним, і багато початківців опускають подвійні лапки навколо змінної і відходять від неї до одного дня ... )
  • Перенаправлення дозволяє читати з файлу як ваш стандартний вхід і записувати стандартний вихід у файл. grep 'foo' <inputfile >outputfileвідкривається outputfileдля запису та inputfileдля читання і передає його вміст як стандартний вхід grep, стандартний вихід якого потім приземляється outputfile. Зазвичай це важко замінити нативним кодом Python.
  • Трубопроводи - це форма перенаправлення. echo foo | nlзапускає два підпроцеси, де стандартний вихід echo- це стандартний вхід nl(на рівні ОС, в Unix-подібних системах, це одна ручка файлу). Якщо ви не можете замінити один або обидва кінці конвеєра нативним кодом Python, можливо, подумайте про використання оболонки зрештою, особливо якщо трубопровід має більше двох-трьох процесів (хоча подивіться на pipesмодуль у стандартній бібліотеці Python або номер більш сучасних та універсальних сторонніх конкурентів).
  • Контроль за роботою дозволяє вам переривати завдання, запускати їх у фоновому режимі, повертати їх на перший план тощо. Основні сигнали Unix для зупинки та продовження процесу, звичайно, доступні і від Python. Але завдання - це абстракція вищого рівня в оболонці, яка включає групи процесів тощо, які ви повинні зрозуміти, якщо ви хочете зробити щось подібне з Python.
  • Цитування в оболонці потенційно плутає, поки ви не зрозумієте, що все в основному є рядком. Так що ls -l /рівнозначно, 'ls' '-l' '/'але цитування навколо літералів абсолютно необов'язково. Рядки без котирування, що містять метахарактори оболонки, зазнають розширення параметрів, токенізації пробілів та розширення підстановки; подвійні лапки запобігають токенізації пробілів та розширенню підстановки, але дозволяють розширювати параметри (заміна змінної, заміна команд та обробка зворотної косої риски). Теоретично це просто, але може набути здивування, особливо якщо є кілька шарів інтерпретації (наприклад, віддалена команда оболонки).

Зрозумійте відмінності між shБашем

subprocessзапускає ваші команди оболонки, /bin/shякщо ви спеціально не вимагаєте іншого (за винятком, звичайно, в Windows, де він використовує значення COMSPECзмінної). Це означає, що різні функції Bash, лише масиви [[тощо , недоступні.

Якщо вам потрібно використовувати синтаксис лише для Bash, ви можете передати шлях до оболонки як executable='/bin/bash'(де, звичайно, якщо ваш Bash встановлений десь в іншому місці, вам потрібно скорегувати шлях).

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessє окремим від свого батька і не може його змінити

Дещо поширена помилка - це щось подібне

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

що крім відсутності елегантності також зраджує принциповому нерозумінню "під" частини назви "підпроцес".

Дочірній процес працює повністю окремо від Python, і коли він закінчується, Python не має уявлення про те, що він робив (крім розпливчастих показників того, що він може зробити висновок про стан виходу та вихід із дочірнього процесу). Дитина, як правило, не може змінити середовище батьків; він не може встановити змінну, змінити робочий каталог або, так багато слів, спілкуватися зі своїм батьком без співпраці з батьком.

Безпосереднім виправленням у цьому конкретному випадку є виконання обох команд в одному підпроцесі;

subprocess.run('foo=bar; echo "$foo"', shell=True)

хоча очевидно, що цей конкретний випадок використання зовсім не вимагає оболонки. Пам'ятайте, ви можете маніпулювати середовищем поточного процесу (а отже, і його дітьми) через

os.environ['foo'] = 'bar'

або передати налаштування оточуючого середовища для дочірнього процесу з

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(не кажучи вже про очевидний рефакторинг subprocess.run(['echo', 'bar']); але, echoзвичайно, це поганий приклад чогось запустити в підпроцесі).

Не запускайте Python від Python

Це трохи сумнівна порада; безумовно, є ситуації, коли це має сенс або навіть є абсолютною вимогою запускати інтерпретатор Python як підпроцес із сценарію Python. Але дуже часто правильний підхід полягає просто importв іншому модулі Python у вашому скрипті виклику та виклику його функцій безпосередньо.

Якщо інший скрипт Python знаходиться під вашим контролем, і це не модуль, спробуйте перетворити його в один . (Ця відповідь вже занадто довга, тому я тут не буду заглиблюватися в деталі.)

Якщо вам потрібен паралелізм, ви можете запускати функції Python в підпроцесах з multiprocessingмодулем. Є також threadingякий запускає кілька завдань в одному процесі (який легший і дає більше контролю, але також більш обмежений тим, що потоки в процесі є щільно з'єднані і пов'язані з одним GIL .)


2
Більш детальне викладення того, як ви можете уникнути виклику Python як підпроцесу, дивіться цю відповідь на дотично подібне питання.
tripleee

4
це монтує мою думку, що мені довелося опублікувати нову відповідь на таке основне питання, щоб показати, як запускати команду з питання ідіоматично. Ваша відповідь довга, але я не бачу такого прикладу. Непов'язане: уникайте вантажу. Якщо check_call () працює у вашому випадку, використовуйте його. Мені довелося виправити код, який використовувався run()наосліп. Відсутність check=Trueвикликала помилку, якої можна було б уникнути, якби було використано check_call - "check" є в імені, ви не можете його втратити - це правильний за замовчуванням: не ігноруйте помилки мовчки. Я не читав далі.
jfs

1
@jfs Дякую за відгук, я насправді планував додати розділ про Bash vs, shале ви побили мене. Я намагаюсь детально розкрити специфіку, щоб допомогти новачкам, для яких ці підводні камені не очевидні, так що вони трохи затягнулися. Вашого має бути цілком достатньо інакше; +1
трійка

Чи stderr/stdout = subprocess.PIPEмають більш високі показники продуктивності, ніж налаштування за замовчуванням?
Стрінгерс

1
@Stringers Я не тестував, але не бачу, чому це повинно. Якщо ви з'єднаєте ці труби з чимось, що робить певну обробку, то, звичайно, для обробки потрібно вимагати; але це не відбувається в самій трубі. За замовчуванням - взагалі не захоплювати stdout або stderr, тобто те, що там надрукується, є поза видимості та контролю Python, як і в os.system().
трійчатка

41

Викликайте це за допомогою підпроцесу

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

Здається, що ви отримуєте помилку через те, що на сервері немає модуля swap, вам слід встановити swap на сервер, а потім запустити сценарій ще раз


3
swapМодуль, очевидно , є, тому що , виконавши команду з робіт оболонки.
Свен Марнах

2
Не на сервері, коли він запускає його на сервері, виникає помилка імпорту.
Якоб Бойер

@mkn: "Тоді я просто скопіював цей висновок і зробив копію вставити в термінал і натисніть клавішу Enter, і це працює ..." - Ви пробували це на сервері чи на вашій машині?
Свен Марнах

Це ви запускаєте це на автономному комп’ютері, але він не працює, коли ви запускаєте його на сервері? Або ви можете запустити його на серверному терміналі, але не на самому сервері
Jakob Bowyer

1
це неправильно. Якщо ви не використовуєте, shell=Trueтоді ви повинні використовувати список для передачі декількох аргументів, тобто використовувати ['a', 'b', 'c']замість 'a b c'. Хоча наївний розкол не спрацює через > file(перенаправлення оболонки) в команді. Більш детально
jfs

18

Можливо, ви використовуєте програму bash з параметром -c для виконання команд:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])

2
subprocess.check_output(bashCommand, shell=True)робить те саме. Якщо ваша команда є статичним рядком, спробуйте самостійно розібрати її у списку та уникайте shell=True; хоча в цьому випадку вам потрібна оболонка для перенаправлення, інакше вам потрібно буде with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
переробити

@tripleee Примітка: /bin/sh(використовується підпроцесом) не обов'язково bash(не можна використовувати басизми). Хоча можна скористатися executable='/bin/bashза бажанням. Ось приклад коду
jfs

2
це перша відповідь, де команда повинна початись успішно (прийнята та 2-а популярні відповіді просто неправильні. Незначна приказка: check_output()тут марно (вихід завжди порожній через > fileперенаправлення; використовуйте check_call()замість цього.
jfs

16

Можна використовувати subprocess , але я завжди відчував, що це не "піфонічний" спосіб. Тому я створив Sultan (безсоромний штекер), який спрощує запуск функцій командного рядка.

https://github.com/aeroxis/sultan


3
Молодці! Набагато чистіший та інтуїтивніший, ніж підпроцес.
mjd2

Дуже дякую! Я радий це почути!
Девід Даніель

2
Це слід чесно прийняти до стандартної бібліотеки.
Джошуа Детвейлер

1
Чи є спосіб зафіксувати вихід з терміналу за допомогою султана?
alvas

Так, ви можете @alvas ... Ось документи про те, як це зробити: sultan.readthedocs.io/en/latest/…
Девід Даніель,

7

Відповідно до помилки, вам не вистачає на сервері пакет з назвою swap . Це /usr/bin/cwmвимагає цього. Якщо ви перебуваєте на Ubuntu / Debian, встановіть python-swapза допомогою aptitude.


але це працює, коли я запускаю його безпосередньо в терміналі ... так що своп повинен бути там, чи не так?
mkn

Є два варіанти. або він не може знайти, swapабо не повинен був імпортувати його в першу чергу. ви можете import swapвручну? це працює?
кічік

гм, я не можу. Якщо я запускаю python із введенням python у терміналі, а потім набираю імпорту swap, тоді я отримав помилку "ImportError: Немає модуля з ім'ям swap". Дивне все-таки те, що воно працює, коли я запускаю команду cwm безпосередньо в терміналі сервера
mkn

Спробуйте надрукувати sys.pathтам, де він працює, а де - ні. Потім спробуйте шукати папку swap або swap.py у надрукованих папках. Як сказав Свен, з цими стежками може виникнути проблема, і це допоможе вам розібратися.
кічик

4

Також ви можете використовувати "os.popen". Приклад:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

Вихід:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None

1
Документація містить велике червоне поле: " Застаріло з версії 2.6: Ця функція застаріла. Використовуйте subprocessмодуль."
трійчатка

1
Справедливо кажучи, os.popenце попередження вже не має, і зараз це просто тонка обгортка subprocess.Popen().
трійчатка

4

Щоб виконати команду без оболонки, передайте команду як список і реалізуйте перенаправлення в Python, використовуючи [subprocess]:

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

Примітка: ні > test.ntв кінці. stdout=fileреалізує перенаправлення.


Щоб запустити команду за допомогою оболонки в Python, передайте команду як рядок та увімкніть shell=True:

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      shell=True)

Ось оболонка відповідає за перенаправлення виводу ( > test.ntє в команді).


Щоб запустити команду bash, яка використовує bashism, вкажіть файл bash явно, наприклад, для імітації підстановки bash-процесу :

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')

Можливо, згадайте, що .split()не є адекватним, коли цитуються рядки тощо. Існує окремий розпорядок, shlex.split()який справляється з довільно складним синтаксисом оболонки.
трійка

@tripleee .split()твори в цьому випадку. shlex.split()може бути корисним іноді, але в деяких випадках це може бути і невдалим. Є дуже багато речей, про які можна було б згадати. Ви можете почати із посилання на описаний вище тег підпроцесу.
jfs

0

Пітонічний спосіб зробити це - використання subprocess.Popen

subprocess.Popen приймає список, де першим елементом є команда, яку слід виконати, а потім будь-які аргументи командного рядка.

Як приклад:

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line

Ні, останній приклад такий самий, як біг echo -v '"Hello Again!"'з одинарними цитатами навколо подвійних лапок.
трійчатка

Також, щоб правильно використовувати subprocesss.Popen, ви повинні керувати отриманим об'єктом процесу (як мінімум, виконати a, wait()щоб запобігти його перетворенню в процес зомбі).
трійка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.