10 привычек хорошего использования UNIX

Автор: | 23 декабря 2009

IBM Logo

Вступление

Когда вы часто используете систему, вы, как правило, приходите к использованию набора шаблонов. Иногда, вы привыкаете делать что-то не лучшим возможным образом. Иногда, вы даже подбираете примеры плохой практики, которые ведут к беспорядку и беспомощности. Одним из лучших способов исправления этих недостатков является добросовестное подбирание хороших привычек, которые их заменят. Эта статья предлагает 10 ценных привычек командной строки UNIX, которые помогут избежать распространенных ошибок в часто используемых командах и сделают вашу работу в командной строке более продуктивной. Каждая привычка более подробно описана ниже в списке хороших практик.

Усвойте 10 хороших привычек

Вот этот список:

  1. Создавайте деревья каталогов в одну строчку
  2. Изменяйте путь к каталогу – не переносите архив
  3. Объединяйте ваши команды с операторами управления
  4. Заключайте переменные в кавычки с осторожностью
  5. Используйте управляющие последовательности для управления длинным вводом
  6. Группируйте ваши команды в список
  7. Используйте xargs вне find
  8. Знайте, когда “grep” должен делать подсчет и когда он должен уступить
  9. Подбирайте определенные поля в выводе, не только строки
  10. Перестаньте перенаправлять поток вывода команды cat на поток ввода других команд

#1 Создавайте деревья каталогов в одну строчку

На листинге 1 показано одну из самых распространённых плохих привычек в UNIX: создание вложенных каталогов (деревьев) по очереди.

Листинг 1. Пример №1: Создание дерева каталогов по одному

~ $ mkdir tmp
~ $ cd tmp
~/tmp $ mkdir a
~/tmp $ cd a
~/tmp/a $ mkdir b
~/tmp/a $ cd b
~/tmp/a/b/ $ mkdir c
~/tmp/a/b/ $ cd c
~/tmp/a/b/c $

Быстрее всего будет использовать опцию -p команды mkdir и сделать все родительские каталоги вместе с дочерними одной командой. Но даже администраторы, которые знают об этой опции, всё ещё создают каталоги по одному. Это стоит вашего времени, чтобы добросовестно выработать хорошую привычку:

Листинг 2. Пример №1: Создание дерева одной командой

~ $ mkdir -p tmp/a/b/c

Вы можете использовать данную опцию, чтобы создать целый комплекс каталогов, а не просто иерархию, это очень удобно при использовании в скриптах. Например:

Листинг 3. Пример №1: Создание комплекса каталогов одной командой

~ $ mkdir -p project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

В прошлом, единственным оправданием для задания каталогов по отдельности было то, что реализация mkdir не поддерживала эту опцию, но это уже не верно в большинстве современных систем. IBM AIX® mkdir, GNU mkdir и другие реализации, которые поддерживают Единую Спецификацию UNIX (Single UNIX Specification), теперь имеют эту опцию.

Для тех систем, которые до сих пор не имеют этой поддержки, можно использовать скрипт mkdirhier, который является обёрткой для mkdir с этой опцией:

~ $ mkdirhier project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

#2. Изменяйте путь к каталогу – не переносите архив

Другим плохим шаблоном является перенос .tar-архива в определённую директорию чтобы распаковать. Вам не нужно этого делать. Вы можете распаковать архив в любую директорию, которую пожелаете, с помощью опции -С. Используйте опцию -С с именем каталога, когда распаковываете архив:

Листинг 4. Пример №2: Использование опции -С при распаковке архива

~ $ tar xvf -C tmp/a/b/c newarc.tar.gz

Привычка использовать -С является предпочтительной, чем перемещение архива для распаковки, изменение каталога и только потом распаковка содержания архива – особенно если архив находится в другом разделе.

#3. Объединяйте ваши команды с операторами управления

Вы, наверное, уже знаете, что в большинстве командных оболочек (shell), можно объединить команды в одной командной строке, поставив точку с запятой (;) между ними. Точка с запятой – это управляющий оператор shell (shell control operator), и хотя она полезна для связывания вместе нескольких дискретных команд в одну командную строку, она не подходит для всего. Например, предположим что вы используете точку с запятой для объединения двух команд, в которых правильное выполнение второй команды полностью зависит от успешного выполнения первой. Если первая команда не выполнилась так, как вы ожидали, то вторая команда всё равно запустится – и не выполнится. Вместо этого, используйте более подходящие управляющие команды (некоторые из них описаны в этой статье). С тех пор, когда ваш shell их поддерживает, они стоят того, чтобы стать привычкой в использовании.

Запускайте команду, только если предыдущая вернула нулевой статус

Используйте управляющий оператор && для объединения двух команд, чтобы вторая выполнилась только тогда, когда первая вернула нулевой статус завершения (zero exit status). Другими словами, если первая команда успешно выполнилась – вторая запустилась. Если же первая команда дала сбой, то вторая не запустится вообще. Например:

Листинг 5. Пример №3. Объединение команд управляющими операторами

~ $ cd tmp/a/b/c && tar xvf ~/archive.tar

В этом примере архив ~/archive.tar распакуется в каталог tmp/a/b/c, если только каталог существует. Если же каталога не существует, то команда tar даже не запустится.

Запускайте команду, только если предыдущая вернула не нулевой статус

Похожим образом, оператор || объединяет две команды, и запускает вторую только тогда, когда первая команда вернула не нулевой код возврата. Другими словами, когда первая команда успешно выполнилась, то вторая – не выполнится. Если произойдёт сбой первой команды, то выполнится вторая. Этот оператор часто используется для проверки существования каталога и создания его, если каталога нет:

Листинг 6. Пример №3: Объединение команд управляющими операторами

~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c

Вы можете также объединять управляющие конструкции, описанные в этой части.

Листинг 7. Пример №3: Пример, объединяющий управляющие операторы

~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c && tar xvf -C tmp/a/b/c ~/archive.tar

#4. Заключайте переменные в кавычки с осторожностью

Всегда будьте внимательны с расширениями оболочки и именами переменных. В целом, хорошая идея заключать вызовы переменных в двойные кавычки, кроме случаев если у вас есть веские причины этого не делать. Аналогично, если у Вас алфавитно-цифровым именем переменной следует текст, убедитесь, что заключили имя переменной в фигурные скобки ({}), чтобы отличить её от ближайшего текста. Иначе, интерпретатор shell примет следуемый текст за имя переменной и, скорее всего, вернёт пустой результат. Листинг 8 содержит примеры различного квотирования переменных и их последствия.

Листинг 8. Пример №4: Квотирование и не квотирование переменных

~ $ ls tmp/
a b
~ $ VAR="tmp/*"
~ $ echo $VAR
tmp/a tmp/b
~ $ echo "$VAR"
tmp/*
~ $ echo $VARa

~ $ echo "$VARa"

~ $ echo "${VAR}a"
tmp/*a
~ $ echo ${VAR}a
tmp/a
~ $

#5. Используйте управляющие последовательности для управления длинным вводом

Вы уже, наверное, видели примеры кода, в котором обратный слеш (\) продолжает длинную строку в следующей, и вы знаете, что большинство shell-оболочек рассматривают ваш ввод последовательных строк, соединённых обратной косой чертой, как одну длинную команду. Тем не менее, Вы не можете пользоваться этой функцией в командной строке, так часто, как вам хочется. Бэкслеш особенно удобен, если ваш терминал не поддерживает многострочное отображение ввода должным образом или когда командная строка меньше чем обычно (например, когда у вас длинный путь в приглашении). Бэкслеш также полезен для лучшего понимания длинных строк по мере ввода, как показано на следующем примере:

Листинг 9. Пример №5: Использование \ для длинного ввода

~ $ cd tmp/a/b/c || \
> mkdir -p tmp/a/b/c && \
> tar xvf -C tmp/a/b/c ~/archive.tar

Кроме того, следующие строки также работают:

Листинг 10. Пример №5: альтернативный пример использования \ для длинного ввода

~ $ cd tmp/a/b/c \
>                 || \
> mkdir -p tmp/a/b/c \
>                    && \
> tar xvf -C tmp/a/b/c ~/archive.tar

Несмотря на то, что вы разделяете вводимую строку на несколько, shell всегда рассматривает её как одну строку, потому что он всегда обрезает все слеши и дополнительные пробелы .

Примечание: В большинстве командных оболочек, когда вы нажимаете клавишу “Вверх”, текущая многострочная команда выводится в виде одной длинной строки.

#6. Группируйте ваши команды в список

Большинство командных оболочек имеют возможность группировать набор команд в списки, таким образом чтобы вы могли передать их общий вывод в поток через пайпы или перенаправить любые из потоков в одно место. Вы можете делать это, запуская список команд в подоболочке (subshell) или управляя списком команд в текущей командной оболочке.

Запуск списка команд в  подоболочке (subshell)

Используйте одиночные скобки для указания списка команд в одной группе. Данное действие запустит команды в подоболочке и позволит вам перенаправлять или собирать весь вывод, как показано в следующем примере:

Листинг 11. Пример №6: Запуск списка команд в новой оболочке

~ $ ( cd tmp/a/b/c/ || mkdir -p tmp/a/b/c && \
> VAR=$PWD; cd ~; tar xvf -C $VAR archive.tar ) \
> | mailx admin -S "Archive contents"

В этом примере, содержание архива распаковывается в tmp/a/b/c/, а вывод из группы команд, в том числе перечень извлеченных файлов, отправляется по почте на адрес admin.

Использование подоболочки предпочтительно, когда вы в списке команд переопределяете переменные окружения и не хотите, чтобы эти определения применились к текущей оболочке.

Запуск списка команд в текущей оболочке

Используйте фигурные скобки ({}) для указания списка команд для запуска в текущей оболочке. Убедитесь, что есть пробелы между скобками и командами или shell может неправильно их интерпретировать. Также, убедитесь, что последняя команда в группе заканчивается точкой с запятой, как в следующем примере:

Листинг 12. Пример №6: Запуск списка комманд в текущеё оболочке

~ $ { cp ${VAR}a . && chown -R guest.guest a && \
> tar cvf newarchive.tar a; } | mailx admin -S "New archive"

#7. Используйте xargs вне find

Используйте инструмент xargs как фильтр, для эффективного использования результатов вывода команды find. В общем случае, результатом выполнения команды find является список файлов, удовлетворяющих заданным критериям. Этот список передается в xargs, который затем запускает некоторые другие полезные команды, используя этот список файлов в качестве аргументов, как показано в следующем примере:

Листинг 13. Пример классического использования xargs

~ $ find some-file-criteria some-file-path | \
> xargs some-great-command-that-needs-filename-arguments

Однако, не думайте, что xargs выступает только как помощник find; это – один из тех недостаточно использованных инструментов, который, когда использование войдёт в привычку, вы захотите применять ко всему, в том числе в следующих целях.

Передача списка, разделённого пробелами

В этом простом вызове, xargs выступает как фильтр, который получает ввод как список (каждый член с новой строки). Утилита вставляет членов списка в одну строку, разделённую пробелами:

Листинг 14. Пример вывода xargs

~ $ xargs
a
b
c
Control-D
a b c
~ $

Вы можете послать вывод любой утилиты, которая выводит имена файлов, через xargs для получения списка аргументов для другой утилиты, которая принимает имена файлов, как аргумент, как в следующем примере:

Листинг 15. Пример использования xargs

~/tmp $ ls -1 | xargs
December_Report.pdf README a archive.tar mkdirhier.sh
~/tmp $ ls -1 | xargs file
December_Report.pdf: PDF document, version 1.3
README: ASCII text
a: directory
archive.tar: POSIX tar archive
mkdirhier.sh: Bourne shell script text executable
~/tmp $

Команда xargs может больше, чем просто передавать списков имен файлов. Используйте её для получения текста в одну строку:

Листинг 16. Пример №7: Использование xargs для вывода текста в одну строку

~/tmp $ ls -l | xargs
-rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 \
root root 238 Dec 03 08:19 README drwxr-xr-x 38 joe joe 354082 Nov 02 \
16:07 a -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rwxr-xr-x 1 \
joe joe 3239 Sep 30 12:40 mkdirhier.sh
~/tmp $
Будьте осторожны с xargs

Технически, существуют редкие ситуации, в которых вы могли бы получить неприятности при использовании xargs. По-умолчанию, символ конца файла – знак подчёркивания (_); если этот символ отправлен как единственный входящий аргумент, всё, что после него – игнорируется. В качестве меры предосторожности, используйте флаг -e, который, без аргументов, отключает строку конца файла полностью.

#8. Знайте, когда “grep” должен делать подсчет и когда он должен уступить

Избегайте отправки вывода команды grep через пайп в wc -l, чтобы подсчитать количество строк вывода. Опция grep -c выводит количество строк, которые совпадают с шаблоном, и она значительно быстрее чем пайп на wc, как в следующем примере:

Листинг 17. Пример №8: Подсчёт строк с использование grep и без

~ $ time grep and tmp/a/longfile.txt | wc -l
2811

real    0m0.097s
user    0m0.006s
sys     0m0.032s
~ $ time grep -c and tmp/a/longfile.txt
2811

real    0m0.013s
user    0m0.006s
sys     0m0.005s
~ $

В дополнение к скорости, опция -c является лучшим способом делать подсчёты. В ситуации с многими файлами, grep с параметром -c возвращает количество строк для каждого файла, по одному на строку, в это время как пайп с wc возвращает общее количество для всех файлов.

Однако, независимо от скорости работы, этот пример демонстрирует другую распространенную ошибку, которой следует избегать. Эти методы подсчёта возвращают количество строк, которые содержат подходящий шаблон – и если это то, что вы ищете, то это прекрасно. Но, в случае, когда строки имеют множественные совпадения с искомым шаблоном, этот метод не даст правильное количество найденных совпадений. Для подсчёта количества совпадений, используйте wc в конце команды. Сначала, запустите команду grep с ключом -o, если ваша версия ее поддерживает. Этот ключ выводит только совпадающие вхождения шаблона, один на строку, но не саму строку. Но вы не можете использовать его вместе с ключом -c, поэтому используйте wc -l для подсчёта строк, как показано в примере:

Листинг 18. Пример №8: Подсчёт подходящих шаблонов

~ $ grep -o and tmp/a/longfile.txt | wc -l
3402
~ $

В этом случае, вызов wc немного быстрее, чем второй вызов grep с пустым шаблоном и подсчётом количества строк (как grep -c).

#9. Подбирайте определенные поля в выводе, не только строки

Инструмент awk предпочтительнее, чем grep, когда Вы хотите найти соответствия только в определенных полях строк вывода, а не в любом месте в строках.

В этом упрощенном примере показано, как получить список только тех файлов, которые изменялись в декабре:

Листинг 19. Пример №9: Использование grep для поиска в заданных полях

~/tmp $ ls -l /tmp/a/b/c | grep Dec
-rw-r--r--  7 joe joe  12043 Jan 27 20:36 December_Report.pdf
-rw-r--r--  1 root root  238 Dec 03 08:19 README
-rw-r--r--  3 joe joe   5096 Dec 14 14:26 archive.tar
~/tmp $

В этом примере, grep выводит строки в которых, время изменения содержит “Dec”, а также выводит и файлы, где в имени встречается “Dec”. Ведь, файл, такой как December_Report.pdf, тоже подходит, несмотря на то, что его изменяли в январе. Это, возможно, не то, что вы хотели. Для нахождения по шаблону в заданном поле, лучше использовать awk, где относительный оператор соответствует точной области поиска, как в примере:

Листинг 20. Пример №9: Использование awk для поиска в заданных полях

~/tmp $ ls -l | awk '$6 == "Dec"'
-rw-r--r--  3 joe joe   5096 Dec 14 14:26 archive.tar
-rw-r--r--  1 root root  238 Dec 03 08:19 README
~/tmp $

Смотрите Ресурсы для лучшего понимания awk.

#10. Перестаньте перенаправлять поток вывода команды cat на поток ввода других команд

Одна из основных-но-общих ошибок использования grep связана с отправкой вывода cat в пайп grep для поиска строки в файле. Это абсолютно излишне и пустая трата времени, потому как утилиты, такие как grep, получают имя файла как аргумент. Вам просто не нужно использовать cat в этом случае, как и в примере:

Листинг 21. Пример №10: Использование grep с использованием cat и без

~ $ time cat tmp/a/longfile.txt | grep and
2811

real    0m0.015s
user    0m0.003s
sys     0m0.013s
~ $ time grep and tmp/a/longfile.txt
2811

real    0m0.010s
user    0m0.006s
sys     0m0.004s
~ $

Эта ошибка относится к множеству инструментов. Поскольку большинство инструментов принимают стандартный ввод в качестве аргумента с использованием дефиса (-), даже аргумент за использование cat для множественной передачи файлов в stdin часто не оправдан. В действительности, такая необходимость есть только для объединения вывода перед передачей в канал, когда вы используете cat с несколькими опциями фильтрования.

Вывод: Перенимайте хорошие привычки

Очень хорошо проверить свои привычки использования командной строки на предмет плохих практик в использовании. Вредные привычки замедляют вас и часто приводят к неожиданным ошибкам. Эта статья представляет 10 новых привычек, которые могут помочь Вам отказаться от многих из наиболее распространенных ошибок. Принятие этих хороших привычек является позитивным шагом на пути к улучшению навыков владения командной строкой UNIX.

Источник: UNIX tips: Learn 10 good UNIX usage habits
via wert2all

Добавить комментарий