
Я уже более десяти лет веду собственные дотфайлы и за это время написал множество скриптов оболочки. В этой статье я решил поделиться с вами теми из них, которые сам применяю регулярно.
Буфер обмена
copy и pasta — это простые обёртки для менеджеров системного буфера обмена вроде pbcopy в macOS и xclip в Linux. Я использую их постоянно.
# Общие примеры.
run_some_command | copy
pasta > file_from_my_clipboard.txt
# Копирование содержимого файла.
copy < file.txt
# Открытие пути файла из буфера обмена.
vim "$(pasta)"
# Декодирование base64 из буфера обмена.
pasta | base64 --decode
pastas выводит текущее состояние буфера обмена в stdout и затем при каждом изменении буфера также выводит новую версию. Этот инструмент я использую где-то раз в неделю.
# Общий пример.
pastas > everything_i_copied.txt
# Скачивает содержимое каждой ссылки, которую я копирую в буфер обмена.
pastas | wget -i -
cpwd копирует адрес текущего каталога в буфер обмена. По сути, это pwd | copy. Я часто так делаю, когда хочу использовать текущий каталог в другой вкладке терминала. То есть я копирую адрес в одной вкладке и перехожу по нему через cd в другой. Пригождается где-то раз в день.
Управление файлами
mkcd foo создаёт каталог и переходит в него через cd. По сути, это команда mkdir foo && cd foo. Я этим скриптом пользуюсь постоянно — почти при каждом создании каталога.
tempe создаёт временный каталог и переключается на него. По существу, это команда cd "$(mktemp -d)". Её я часто использую, когда нужно проделать какую-то временную работу, чтобы потом вручную всё не подчищать. Вот пара типичных примеров:
# Скачивание и извлечение файла.
tempe
wget 'https://example.com/big_file.tar.xz'
tar -xf big_file.tar.xz
# ...какие-то действия с файлом...
# Написание быстрого одноразового скрипта для экспериментов.
tempe
vim foo.py
python3 foo.py
trash a.txt b.png отправляет a.txt и b.png в корзину. Работает в macOS и Linux. Этим скриптом я пользуюсь каждый день, причём явно чаще, чем rm. Полезная штука — защищает от случайного удаления файлов.
mksh позволяет быстро создавать скрипты оболочки. К примеру, mksh foo.sh создаёт foo.sh, делает его исполняемым с помощью chmod u+x, добавляет префиксы Bash и открывает скрипт в редакторе (в моём случае Vim). Пригождается раз в несколько дней. Многие из перечисленных в этой статье скриптов были созданы с помощью mksh.
Интернет
serveit запускает на порту localhost:8000 статический файловый сервер, предоставляющий доступ к файлам текущего каталога. По сути, это команда python3 -m http.server 8000, только в этом случае скрипт обрабатывает сценарии, когда Python не установлен, используя вместо него другие программы. Мне этот инструмент пригождается несколько раз в неделю. Если вы не веб-разработчик, то вам он может оказаться не так полезен.
getsong использует yt-dlp для скачивания музыки в максимальном доступном качестве с таких ресурсов, как YouTube или SoundCloud. Например, getsong https://www.youtube.com/watch?v=dQw4w9WgXcQ скачает указанный ролик в виде трека. Я пользуюсь этим скриптом несколько раз в неделю. Обычно для скачивания саундтреков к играм.
getpod также с помощью yt-dlp скачивает какие-нибудь записи для проигрывателя подкастов. Пригождается несколько раз в месяц.
getsubs скачивает английские субтитры к видео. Этот скрипт хорош тем, что сначала ищет «официальные» субтитры и только потом использует сгенерированные. Иногда я считываю субтитры вручную, иногда выполняю getsubs https://video.example/foo | ollama run llama3.2 "Summarize this", а иногда использую этот скрипт просто для генерации описания к видео, которое не хочу сохранять на ПК. Пригождается раз в несколько дней.
wifi off, wifi on и wifi toggle помогают с управлением WiFi. wifi toggle я обычно использую, когда возникают траблы с интернетом. Благо, в этих целях пригождается он где-то всего раз в месяц.
url "$my_url" парсит URL-адрес на составляющие части. Использую примерно раз в месяц для получения информации об URL, зачастую просто потому, что не хочу кликать по ссылке с трекером.
url 'https://evil.example/track-user-link?url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba#cookie=123'
# original: https://evil.example/track-user-link?url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba#cookie=123
# protocol: https
# hostname: evil.example
# path: /track-user-link
# query: url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba
# - url https://url-i-want-to-visit.example
# - track 06f8582a-91e6-4c9c-bf8e-516884584aba
# hash: cookie=123
Обработка текста
line 10 выводит строку 10 из stdin. Например, cat some_big_file | line 10 выводит 10 строку файла. Думаю, такая возможность должна быть встроенной — как команды head и tail. Использую её где-то раз в месяц.
scratch открывает временный буфер Vim. По сути, это алиас для $EDITOR $(mktemp). Пригождается почти каждый день для оперативной обработки текста или для написания временной заметки.
straightquote преобразует «умные кавычки» в “прямые”. Вообще, меня этот нюанс не особо волнует, но иногда такие кавычки просачиваются в код, с которым я работаю. Кроме того, их изменение в прямые позволяет чуть уменьшить размер файла, что порой весьма актуально. Мне эта функция пригождается как минимум раз в неделю.
markdownquote добавляет > перед каждой строкой. Пользуюсь этим скриптом в Vim постоянно. Я выбираю нужный фрагмент и выполняю :'<,'>!markdownquote, чтобы сделать выбранный текст цитатой. Пригождается где-то раз в неделю.
length foo возвращает 3 (пожалуй, нужно просто использовать wc -c.)
jsonformat получает JSON из stdin и в красивом виде выводит через stdout. Использую этот скрипт несколько раз в год.
uppered и lowered преобразуют строки в нижний и верхний регистр. Например, echo foo | uppered возвращает FOO. Пригождается раз в неделю.
nato bar возвращает Bravo Alfa Romeo. Обычно использую при общении со службой поддержки, когда нужно вывести длинную строку из букв и цифр, что за всю жизнь мне приходилось делать всего несколько раз. Но иногда выручает!
u+ 2025 возвращает ñ, LATIN SMALL LETTER N WITH TILDE. Быстрый способ поиска строки Юникода. Этот скрипт пригождается редко — может, раз в месяц.
snippets foo выводит содержимое ~/.config/evanhahn-snippets/foo. Я использую snippet arrow в качестве →, snippet recruiter в качестве быстрого ответа рекрутёрам “not interested”, snippet lorem для вывода блока “Lorem ipsum” и ещё несколько других. В неделю пригождается пару раз.
Лаунчеры REPL
Вдохновлённый интерактивной оболочкой REPL в Ruby, я создал несколько лаунчеров:
iclj запускает Clojure REPL,
ijs запускает Deno REPL (или Node REPL, если Deno отсутствует),
iphp запускает PHP REPL,
ipy запускает Python REPL,
isql запускает оболочку SQLite (алиас для
sqlite3 :memory:).
Дата и время
hoy выводит текущую дату в формате ISO, например, 2020-04-20. Им я пользуюсь постоянно, так как люблю добавлять в начало названий файлов текущую дату.
timer 10m запускает таймер на 10 минут, затем воспроизводит рингтон и отправляет системное уведомление (подробнее в notify ниже). Я также регулярно использую команду bb timer 5m, чтобы запустить пятиминутный таймер в фоновом режиме (подробнее о bb ниже). В целом этот скрипт пригождается мне почти каждый день в качестве эффективного инструмента отслеживания времени.
rn выводит текущее время и дату, используя date и cal. Пригождается где-то раз в неделю. Вот пример его выдачи:
4:20PM on Wednesday, October 22, 2025
September 2025
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Аудио, видео и изображения
ocr my_image.png извлекает из изображения текст и выводит его в stdout. К сожалению, работает только в macOS, но я планирую это исправить. (Вот отдельная статья, посвящённая этому скрипту).
boop (алиас, не скрипт оболочки). Если команда была выполнена успешно, издаёт мажорный звук, в противном случае — минорный. Я применяю его, например, так: run_the_tests ; boop, чтобы понять, успешно ли завершились тесты. Он также пригождается при выполнении длительных команд, так как об их завершении уведомлений не поступает. Использую его постоянно.
sfx foo, по сути, просто воспроизводит ~/.config/evanhahn-sfx/foo.ogg. Применяется в описанных выше boop и timer.
tunes воспроизводит аудио из файла с помощью mpv. Использую постоянно через команду --shuffle ~/music.
pix показывает изображение с помощью mpv. Пригождается несколько раз в неделю для просмотра фото.
radio — небольшая обёртка для моих любимых станций radio lofi и radio salsa. Использую несколько раз в месяц.
speak считывает данные из stdin, удаляет всё форматирование Markdown и передаёт их в механизм речевого синтеза (say в macOS и espeak-ng в Linux). Пользуюсь этим несколько раз в месяц, обычно, когда нет возможности вычитать собственный текст вслух.
shrinkvid — обёртка ffmpeg, немного сжимающая видео. Пригождается где-то раз в месяц.
removeexif удаляет из JPEG данные EXIF. Пользуюсь этой штукой редко, отчасти, потому что она ��е удаляет EXIF из файлов других форматов вроде PNG. Но я рассчитываю однажды её доработать, поэтому пока держу на вооружении.
tuivid — этот скрипт позволяет смотреть видео в терминале, но я его почти не использую. Хотя при всей своей странности он мне нравится.
Управление процессами
each — мой ответ xargs и find ... -exec, которые я нахожу сложными в работе. Например, ls | each 'du -h {}' выполняет du -h для каждого файла в каталоге. Пользуюсь этим решением я редко, но регулярно мучаюсь с xargs, так что это неплохая альтернатива.
running foo аналогичен ps aux | grep foo, но намного читабельнее (лично для меня) — просто выделенный фиолетовым PID и команда.
murder foo or murder 1234 — это обёртка вокруг kill, которая отправляет kill -15 $PID, недолго ожидает, отправляет kill -2, ожидает, отправляет kill -1, и ещё раз ожидает, прежде чем в завершение отправить kill -9. Если я хочу остановить программу, то сначала прошу её об этом вежливо и уже потом грублю. Пригождается несколько раз в месяц.
waitfor $PID — ожидает завершения PID и только потом продолжает выполнение. Также не позволяет системе уйти в режим сна. Я этим скриптом пользуюсь где-то раз в месяц, когда:
# Хочу запустить что-либо только после завершения другого процесса.
waitfor 1234 ; something_else
# Запустил длительный процесс и хочу знать, когда он завершится.
waitfor 1234 ; notify 'process 1234 is done'
bb my_command подобна my_command &, только выполняет команду в глубоком фоновом режиме, совершенно не требуя вашего внимания. Пригождается, когда вам нужно запустить демона или длительный процесс, дальнейшее выполнение которого вы отслеживать не хотите. Чаще всего я применяю этот макрос в виде bb ollama serve и bb timer 5m. В целом пригождается где-то раз в день.
prettypath выводит $PATH, но с разделением строк пробелами для повышения читабельности. Использую этот скрипт в основном при отладке проблем с $PATH, которые случаются редко. Зато, когда такое происходит, он очень выручает.
tryna my_command выполняет my_command, пока та не завершится успешно. trynafail my_command, напротив, выполняет my_command, пока та не провалится. Пригождается редко, но в некоторых случаях помогает. tryna wget ... будет пытаться скачать указанный элемент. trynafail npm test останавливает тесты, когда те начинают проваливаться.
Быстрый доступ
emoji помогает находить эмодзи. Например, emoji cool выводит:
?
?
?
?
?
httpstatus выводит все коды HTTP-состояния. httpstatus 204 выводит 204 No Content. Так как я веб-разработчик, этот скрипт пригождается мне несколько раз в месяц, избавляя от необходимости искать ответ в сети.
alphabet просто выводит английский алфавит в верхнем и нижнем регистре. Кстати, использую я этот инструмент на удивление часто (где-то раз в месяц). Вот его вывод:
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Управление системой
theme 0 меняет тему системы на тёмную, а theme 1 — на светлую. Причём этот скрипт меняет не только тему ОС, но и темы Vim, Tmux и терминала. Пригождается минимум раз в день.
sleepybear отправляет систему в сон. Работает в macOS и Linux. Использую несколько раз в неделю.
ds-destroy рекурсивно удаляет все файлы .DS_Store в каталоге. Мне не нравится, что macOS захламляет ими папки. Пользуюсь этим скриптом редко, но при необходимости он весьма выручает.
Всякая всячина
catbin foo — это, по сути, cat "$(which foo)". Позволяет просматривать исходный код файла, указанного в PATH (к примеру, я его использовал для написания этой статьи). Пригождается пару раз в месяц.
notify отправляет уведомление от ОС. Применял его в некоторых других скриптах (см. выше). Плюс примерно раз в месяц использую его как-то так:
run_some_long_running_process ; notify 'all done'
uuid выводит UUID v4. Применяю где-то раз в месяц.
А какие скрипты часто используете вы? Поделитесь
Я здесь перечислил лишь те, которыми много пользуюсь сам. Надеюсь, некоторые из них пригодятся и вам.
Комментарии (9)

ic10
02.11.2025 09:08Для меня большая проблема с личными скриптами -- запомнить их имена. Иногда то, что они вообще существует :)
Я придумал лайфхак, который может кому-то будет полезен -- имитация пространства имен. Допустим есть какая-то область деятельности, скажем, управление iptables. Называем все свои скрипты/алиасы через
iptables.action
И теперь, когда вы наберете iptables.<TAB>, шелл покажет для автодополнения все ваши скрипты/алиасы и не надо их все запоминать.
Ну и также для других областей, или все в один неймспейс загнать вроде my.<TAB>

Alex_Sage
02.11.2025 09:08Вывод в rn красиво выглядит.
Я на работе всплывашку с таким календарем добавил для сайта, скопировал из своей утилиты. Теперь осталось только для консоли её переписать, чтобы календари прямо везде были ))

aragaer
02.11.2025 09:08Когда увидел упоминание bb, то ожидал, что дальше будет babashka. Из волшебных скриптов у меня есть
xscr -- scrot + xclip + удалить файл (т.е. скриншот сразу в буфер обмена)
mpvs -- mpv на содержимое буфера обмена (обычно ссылка на ютуб)
ecc -- emacslient -c
gpg-check, gpg-unlock -- проверка того, что пассфраза закеширована в агенте и соответственно "запросить пассфразу, чтобы ее закешировать"
pyshell -- проверяет наличие .venv, если нет, то создает, а потом создает subshell и в нем этот venv активирует (аналог pipenv shell)
tmux-attach (и алиас ta) -- написанная на кложе (собственно bb) обвязка для tmux

Newpson
02.11.2025 09:08xscr -- scrot + xclip + удалить файл (т.е. скриншот сразу в буфер обмена)
вам не кажется расточительным создавать файл лишь ради того, чтобы через мгновение удалить? или он создаётся в tmpfs?

ThingCrimson
02.11.2025 09:08Спасибо за интересные примеры скриптописательства; и большое спасибо за идею выстроить из этого добра некую свою экосистему! Поставил себе в todo перешерстить свои скрипты с подобным подходом.

0x00FA7A55
02.11.2025 09:08Вот мой скриптик чтоб из двух ямликов вытаскивать только разницу. Бывает полезен когда, например, делаешь манифесты для fluxcd и хочешь чтоб там был только минимальный набор инфы.
Скрытый текст
#!/usr/bin/env python3 """ Compare two YAML files and generate a patch with differences. Usage: ./get-yaml-diff.py default-values.yaml custom-values.yaml """ import sys import yaml from typing import Any def find_differences(base: dict[str, Any], custom: dict[str, Any], path: str = "") -> dict[str, Any]: """Recursively find differences between two dictionaries.""" differences: dict[str, Any] = {} keys = set(base) | set(custom) for key in sorted(keys): full_path = f"{path}.{key}" if path else key if key not in base: differences[full_path] = {'default': None, 'custom': custom[key]} elif key not in custom: differences[full_path] = {'default': base[key], 'custom': None} else: base_val, custom_val = base[key], custom[key] if isinstance(base_val, dict) and isinstance(custom_val, dict): differences.update(find_differences(base_val, custom_val, full_path)) elif base_val != custom_val: differences[full_path] = {'default': base_val, 'custom': custom_val} return differences def create_patch(differences: dict[str, Any]) -> dict[str, Any]: """Convert flat differences dictionary to nested patch structure.""" patch: dict[str, Any] = {} for dotted_path, info in differences.items(): value = info['custom'] if value is None: continue current = patch parts = dotted_path.split('.') for key in parts[:-1]: current = current.setdefault(key, {}) current[parts[-1]] = value return patch def load_yaml(filename: str) -> dict[str, Any]: """Load YAML file and return its content as a dictionary.""" try: with open(filename, 'r') as f: return yaml.safe_load(f) or {} except FileNotFoundError: print(f"Error: File '{filename}' not found.", file=sys.stderr) sys.exit(1) except yaml.YAMLError as e: print(f"Error parsing YAML file '{filename}': {e}", file=sys.stderr) sys.exit(1) def main(): """Load YAML files, compute differences, and print patch.""" if len(sys.argv) != 3: print(f"Usage: {sys.argv[0]} default-values.yaml custom-values.yaml", file=sys.stderr) sys.exit(1) default_file, custom_file = sys.argv[1], sys.argv[2] default_values = load_yaml(default_file) custom_values = load_yaml(custom_file) differences = find_differences(default_values, custom_values) patch = create_patch(differences) print(yaml.dump(patch, allow_unicode=True, sort_keys=False, default_flow_style=False, indent=2)) if __name__ == "__main__": main()

UnknownUserMax
02.11.2025 09:08Какие-то сомнительные скрипты. Будто человек только-только начал разбираться в командах консоли.
evgenyk
Спасибо! Много интересных идей.