Пишемо shell скрипти на Python і чи можна замінити їм Bash

У цій невеликій статті піде мова про те, чи можна легко використовувати Python для написання скриптів замість Bash/Sh. Перше питання, яке виникне у читача, мабуть, а чому, власне, не використовувати Bash/Sh, які спеціально для цього створені? Створені вони були досить давно і, на мій погляд, мають досить специфічний синтаксис, не сильно схожий на інші мови, який досить складно запам'ятати, якщо ви не адміністратор 50+ левела. Пам'ятайте, ви навскидку як написати на ньому простий if?

if [ $# -ne "$ARGCOUNT" ]
then
echo "Usage: `basename $0` filename"
exit $E_WRONGARGS
fi

Елементарно правда? Інтуїтивно зрозумілий синтаксис. :)

Тим не менш в python ці конструкції набагато простіше. Кожен раз коли я пишу щось на баше, то неодмінно лізу в пошуковик щоб згадати як писати простий if, switch або щось ще. Привласнення я вже запам'ятав. :) В Python все інакше. Я хоч і не пишу на ньому цілу добу, але ніколи не доводилося лізти і дивитися як там зробити простий цикл, тому що синтаксис мови простий і інтуїтивний. Плюс до всього він набагато ближче до решти мейнстрімових мов типу java або c++, ніж Bash/Sh.

Також у стандартній та інших бібліотеках Python є набагато більш зручні бібліотеки ніж консольні утиліти. Скажімо, ви хочете розпарсити json, xml, yaml. Знаєте, який я нещодавно бачив код в баше щоб зробити це? Правильно:

python -c "import json; json.loads..." :)

І це був не мій код. Це був код баше/питоно нейтрального людини.

Те ж саме з регексом, sed безперечно зручна утиліта, але як багато людей пам'ятає, як правильно її використовувати? Ну крім E. Lee McMahon, який її створив. Та впринципі багато хто пам'ятає, навіть я пам'ятаю як робити прості речі. Але, на мій погляд, в Python модуль re набагато зручніше.

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

Велкам під кат.

Введення
Shell python нічим не відрізняється від простого Python крім однієї деталі. Вирази всередині grave accent символів ( ` ) на відміну від Python не є eval, а позначає виконання команди в шелле. Наприклад,

`ls -l`

виконає
ls -l
як shell команду. Також можливо написати все це без ` в кінці рядка

`ls -l

і це теж буде коректним синтаксисом.

Можна виконувати відразу кілька команд на різних рядках

`
echo test > test.txt
cat test.txt
`

і команди, що займають кілька рядків

`echo This is \
a very long \
line

Виконання кожного виразу в shellpy повертається об'єкт класу Result

result = `ls -l

Це можна бути або Result або InteractiveResult (Посилання на гитхаб з документацією, можна і потім подивитися :) ). Давайте почнемо з простого результату. З нього можна легко отримати код повернення виконаної команди

result = `ls -l
print result.returncode

І текст з stdout і stderr

result = `ls -l
result_text = result.stdout
result_error = result.stderr

Також можна пробігтися по всіх рядках stdout виконаної команди в циклі

result = `ls -l
for line in result:
print line.upper()

і так далі.

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

result = `ls -l
if result:
print 'Return code for ls -l was 0'

Або ж більш простим способом отримати текст з stdout

result = `ls -l
print result

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

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

from sh import ifconfig
print(ifconfig("wlan0"))

Але мене такий синтаксис не влаштовував, оскільки навіть незважаючи на складності, хотілося отримати best user experience ©, а для мене це означає наскільки це можливо просте і близьке до його величності Шеллу написання команд.

Знайомий з темою читач запитає, чим IPython то тебе не влаштував, там ж майже як у тебе тільки значок інший ставити треба, може ти просто велосипедист, якому ліньки зазирнути в пошуковик? І правда він виглядає ось так:

lines = !ls -l

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

import myipythomodule

і щоб все відразу запрацювало як у казці. Єдиний спосіб переиспользовать скрипт, це виконати його. Після виконання в оточенні у тебе з'являються всі функції та змінні, оголошені в виконуваному файлі. Не кошерно на мій погляд.

У shellpy код переиспользуется легко і імпортується точно так само як і в звичайному python. Припустимо, у нас є модуль common в якому ми зберігаємо дуже корисний код. Заглянемо в директорію з цим модулем

ls common/
common.spy __init__.spy

Отже, що у нас тут є, ну по-перше init, але з розширенням .spy. Це і є відмінною рисою spy модуля від звичайного. Подивимося також всередину файлу common.spy, що там цікавого

def common_func():
return `echo 5

Ми бачимо що тут оголошена функція, яка всередині себе використовує shellpy синтаксис щоб повернути результат виконання `echo 5. Цей модуль використовується у коді? А ось як

from common.common import common_func

print('Result of imported function is' + str(common_func()))

Бачите? Як у звичайному Python, просто взяли і заимпортировали.

Як же все працює. Це працює за допомогою PEP 0302 — Import New Hooks. Коли ви імпортуєте щось у своєму коді то спочатку Python запитує у хука, чи немає тут чогось свого, хук переглядає PYTHONPATH на наявність файлів *.spy або модулів shellpython. Якщо нічого немає, то так і каже: "Нічого нема, импортируй сам". Якщо ж він знаходить щось там, то хук займається імпортом самостійно. А саме, він робить препроцессинг файлу в звичайний python і складає все це добро в temp директорію операційної системи. Записавши новий Python файл або модуль він додає його в PYTHONPATH і за справу береться вже самий звичайний імпорт.

Давайте ж скоріше подивимося на який-небудь приклад
Цей скрипт завантажує аватар юзера Python Github і кладе його в temp директорію

import json
import os
import tempfile

# за допомогою curl отримує відповідь від апі гитхаба
answer = `curl https://api.github.com/users/python

# синтаксичний цукор щоб порівняти результат виконання з нулем
if answer:
answer_json = json.loads(answer.stdout)
avatar_url = answer_json['avatar_url']

destination = os.path.join(tempfile.gettempdir(), 'python.png')

# в цей раз завантажуємо саму картинку
result = `curl {avatar_url} > {destination}
if result:
# якщо проблем не виникло, то показуємо картинку 
p ' ls -l {destination}
else:
print('Failed to download avatar')

print('Avatar downloaded')
else:
print('Failed to access github api')

Краса…

Установка
Shellpython можна встановити двома способами:
pip install shellpy
або склонировав репозиторій і виконавши
setup.py install
. Після цього у вас з'явиться утиліта
shellpy
.

Запустимо що-небудь
Після установки можна потестувати shellpython на прикладах, які доступні прямо в репозиторії.

shellpy example/curl.spy

shellpy example/git.spy

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

shellpy example/allinone/test.spy

Для третього Python команда виглядає ось так

shellpy example/allinone/test3.spy

Сумісність
Це працює на Linux і має працювати на Mac для Python 2.x і 3.x. На віндовсі поки не працює, але жодних проблем для роботи немає, так як все писалося з використанням кросплатформених бібліотек і нічого платформоспецифичного в коді немає. Просто не дійшли руки ще, щоб потестувати на віндовсі. Маку у мене теж немає, але начебто в одного працювало :) Якщо у вас є мак і у вас все нормально, скажіть будь ласка.

Якщо знайдете проблеми — пишіть в комент, або сюди Join the chat at https://gitter.im/lamerman/shellpy або телеграфіруйте як-небудь :)

Документація (англійською)
Wiki

Можна законтрибьютить
Звичайно :)

Воно мені нічого в продакшені не разломает?
Зараз версія 0.4.0, це не стейбл і продакшн процеси поки краще не зав'язувати на скрипт, почекавши поки все налагодиться. Але в девелопменті, CI можна використовувати цілком. Все це покрито тестами і працює :) Build Status

P. s.
Пишіть ваші відгуки про ідею в цілому і про реалізацію зокрема, а також про проблеми, побажання, всіх радий почути :) Заводите Issues ще в гітхабі, там їх вже багато :)

Джерело: Хабрахабр
  • avatar
  • 0

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.