Что ожидается от скриптового языка:

  1. умеет работать с файлами (прочитать/записать csv/yaml/json)
  2. умеет работать с сетью (отправить http-запрос)
  3. сделать какую-то логику
  4. не требует установки окружения и компиляции
  5. все в одном файле

BASH удовлетворяет всему этому, но у него свои проблемы:

  1. Тяжело прочитать
  2. Тяжело отлаживать
  3. Тяжело писать нетривиальную логику

Казалось бы Python позволяет решить все эти проблемы, но очень быстро на Питоне хочется ставить какие-нибудь пакеты.

Как это сделать поудобнее?

  1. Ставим poetry.
  2. Создаем папку для скрипта: mkdir my-script-1
  3. Инициализируем там poetry: cd my-script-1 && poetry init. Или берем готовый файл pyproject.toml:
[tool.poetry]
package-mode = false

[tool.poetry.dependencies]
python = "^3.12"
pandas = "^2.2.1"
requests = "^2.31.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
  1. Создаем файл для нашего скрипта: touch main.py && chmod +x main.py
  2. Скачиваем зависимости: poetry install --no-root (или просто poetry install, если выставлено свойство package-mode=false)
  3. Заходим в виртуальное окружение (от туда уже можно запускать скрипт): poetry shell, или напрямую запускаем poetry run ./main.py

Сам скрипт (просто стартовый пример):

#!/usr/bin/env python3

# https://docs.python.org/3/library/dataclasses.html
from dataclasses import dataclass
# https://pandas.pydata.org/
import pandas as pd
# https://requests.readthedocs.io/en/latest/
import requests
from numpy import ndarray


@dataclass
class DocTags:
    id: str
    tags: [str]


def parse_line(values: ndarray) -> [DocTags]:
    assert values.size == 2
    doc_id: str = values[0]
    tags_str: str = values[1]
    tags = list(map(str.strip, tags_str.split(",")))
    return DocTags(doc_id, tags)


def read_file(filename: str) -> [DocTags]:
    data = pd.read_csv(filename, sep="\t", header=None)
    return list(map(parse_line, data.values))


def main():
    docs = read_file("example.tsv")
    for doc in docs:
        with open(f"out/{doc.id}.txt", 'w') as file:
            file.write(str(doc) + "\n")
            response = requests.put(f"https://httpbin.org/anything/{doc.id}", json=doc.tags)
            file.write(f"status_code={response.status_code} json={response.text}\n")


if __name__ == '__main__':
    main()

Естественно, это все открывается в PyCharm: там можно запускать и вставать в отладку.

В итоге, все-таки есть дополнительные шаги, но их не так уж и много.

PS. Домашнее задание: написать то же, но на чистом BASH. У меня получилось 22 строчки, но сложно читаемых и не поддерживаемых. Впрочем, для скриптов это не особо надо. Тем не менее, это заставило задуматься почему изначально все-таки выбрал BASH, а не Python.

PPS. Домашнее задание 2: переписать скрипт так, чтобы внешние библиотеки (pandas и requests не требовались – по идее это должно быть просто). Решение, если писать не хочется.

PPPS. Можно еще то же самое посмотреть на Kotlin (link1, link2), Ruby и других языках.