Грамотное владение shell — один из самых важных навыков, которыми вы как программист должны обладать. Unix shell — одна из самых мощных идей, реализованных в коде, и она должна стать вашей второй натурой. Ни один другой инструмент и близко не сравним с возможностью быстрого выполнения сложных задач или с сохранением этих команд в виде скриптов.

В своей работе я использую Vim в качестве редактора, а Unix — в качестве «IDE». Я не модифицирую свой vimrc, чтобы добавить в него функции IDE; самый важный плагин, который использую ежедневно — это Ctrl+P, и он нужен мне только для упрощения открытия файлов. Грамотное владение Vim — ценный навык, но важно понимать, когда от него нужно отказаться. В своей повседневной работе я взаимодействую с несколькими терминалами: обычно в одном из них есть Vim, второй используется для запуска сборок или демонов, а в третьем запущен shell, способный выполнить любые мои команды.



Постоянно открытый shell позволяет мне выполнять сложные задачи и отвечать на сложные вопросы. Интересные вещи я нахожу при помощи git grep, масштабные операции поиска и замены я выполняю через sed, отвечаю на вопросы с помощью awk, а более тонкие задачи я выполняю создаваемыми по ходу работы командами и конвейерами shell. Я обладаю свободой творческого решения задач без ограничений, заложенных проектировщиками IDE.

Вот пример задачи, с которой я недавно столкнулся: у меня было множество изменений в репозитории git. Я хотел восстановить удалённые файлы, не теряя другие свои изменения, но их были сотни. Как оптимальнее всего будет решить эту проблему?

Я начал с оценки масштаба проблемы при помощи команды git status, которая показала мне сотни удалённых файлов, которые необходимо было восстановить. Понятно, что такие объёмы работы вручную выполнять непрактично, поэтому я ввёл git status -s, чтобы получить более удобные для конвейера результаты.

$ git status -s
 D main/a52dec/APKBUILD
 D main/a52dec/a52dec-0.7.4-build.patch
 D main/a52dec/automake.patch
 D main/a52dec/fix-globals-test-x86-pie.patch
 D main/aaudit/APKBUILD
 D main/aaudit/aaudit
 D main/aaudit/aaudit-common.lua
 D main/aaudit/aaudit-repo
 D main/aaudit/aaudit-server.json
 D main/aaudit/aaudit-server.lua
 ...

С этим уже можно работать. Я добавил grep '^ D', чтобы отфильтровать все строки, которые не были удалены, и пропустил остальные через awk '{ print $2 }', чтобы извлечь только имена файлов. Часто я запускаю незавершённый конвейер, чтобы проверить свою работу:

$ git status -s | grep '^ D' | awk '{ print $2 }'
main/a52dec/APKBUILD
main/a52dec/a52dec-0.7.4-build.patch
main/a52dec/automake.patch
main/a52dec/fix-globals-test-x86-pie.patch
main/aaudit/APKBUILD
main/aaudit/aaudit
main/aaudit/aaudit-common.lua
main/aaudit/aaudit-repo
main/aaudit/aaudit-server.json
main/aaudit/aaudit-server.lua
...

Очень хорошо — мы создали список файлов, с которыми нужно работать. Стоит заметить, что можно было бы не использовать grep и получить тот же результат при помощи одного awk:

$ git status -s | awk '/^ D/ { print $2 }'
main/a52dec/APKBUILD
main/a52dec/a52dec-0.7.4-build.patch
main/a52dec/automake.patch
main/a52dec/fix-globals-test-x86-pie.patch
main/aaudit/APKBUILD
main/aaudit/aaudit
main/aaudit/aaudit-common.lua
main/aaudit/aaudit-repo
main/aaudit/aaudit-server.json
main/aaudit/aaudit-server.lua
...

Однако сейчас мы просто пишем «одноразовую» команду для решения конкретной временной проблемы, поэтому её совершенство для нас не так важно. Никто не будет проверять эту команду. В таких ситуациях я часто решаю по одной проблеме за раз: «отфильтровываем список» и «преобразуем список». Как бы то ни было, последним шагом будет использование этого списка файлов для решения проблемы при помощи команды xargs.

$ git status -s | awk '/^ D/ { print $2 }' | xargs git checkout --

Давайте рассмотрим ещё несколько примеров интересных одноразовых конвейеров shell.
Естественно, для поиска этих примеров я написал конвейер shell:

$ history | cut -d' ' -f2- | awk -F'|' '{ print NF-1 " " $0 }' | sort -n | tail

Что здесь происходит:

  • history выводит список истории моих команд shell.
  • cut -d' ' -f2- удаляет первое поле из каждой строки, используя в качестве разделителя пробел. history нумерует каждую команду, а эта часть удаляет номер.
  • awk -F'|' '{ print NF-1 " " $0 } приказывает awk использовать в качестве разделителя полей каждой строки символ | и вывести в качестве префикса каждой строки количество полей. Эта команда выводит каждую строку из моей истории с префиксом — количеством вхождений оператора конвейера в этой строке.
  • sort -n сортирует этот список численно.
  • tail выводит последние 10 элементов.

Эта команда, написанная за считанные секунды, находит, характеризует, фильтрует и сортирует мою историю shell по сложности команд. Вот пара крутых команд shell, которые мне удалось найти:

Воспроизведение 50 самых новых видео в папке при помощи mpv:

ls -tc | head -n50 | tr '\n' '\0' | xargs -0 mpv


Я постоянно использую эту команду. Если я хочу посмотреть видео позже, то использую для этого файла touch, чтобы он отображался в начале этого списка. Ещё одна команда передаёт при помощи netcat tarball пропатченной версии Celeste моему другу, за исключением (больших) ресурсов игры. Процесс передачи отображается при помощи pv:

find . ! -path './Content/*' | xargs tar -cv | pv | zstd | nc 204:fbf5:... 12345


А вот, что происходит на стороне моего друга:

nc -vll :: 12345 | zstdcat | pv | tar -xv

Кстати, tar — это недооценённый инструмент для перемещения групп файлов по конвейеру. Он может считывать и записывать tarball в stdin и stdout!

Надеюсь, эта статья дала вам представление о мощи Unix shell. Если вы хотите больше узнать о командной оболочке, то рекомендую shellhaters.org в качестве введения в различные части спецификации POSIX, связанные с shell. Не бойтесь спецификации — это краткий, исчерпывающий и понятный текст, в котором полно примеров. Ещё я крайне рекомендую уделить время изучению конкретно awk: вот краткий туториал.



На правах рекламы


Серверы для разработчиков и не только! Аренда виртуального сервера на базе новейших процессоров AMD и Intel, хранилище на основе NVMe дисков для размещения проектов любой сложности, создавайте собственную конфигурацию сервера в пару кликов!

Подписывайтесь на наш чат в Telegram.