[Перевод] Для программиста 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.

