Тег «bash»

Bash как mainstream-язык

Нет, я не буду здесь расползаться пространными рассуждениями, как я обычно это делаю. Просто две ссылки:

Получить полный путь к файлу/каталогу в bash

Иногда в аргументах сценария приходят пути к файлам и каталогам в разнообразных форматах, ну например:

./file1
~/cat1/cat2/
../../cat1/file2
justfile
cat1/file3

А хочется в каждом случае получить полный путь, скажем, «/home/user/cat1/file». И если второй вариант раскроется оболочкой в полный путь еще до передачи сценарию, то остальные так и останутся с точками и относительными путями.

Есть простое и изящное решение, подсмотренное на каких-то форумах:

absPath=$(readlink -f "$(dirname "$relPath")")/$(basename "$relPath")

Эта штука замечательно работает, но ровно до тех пор, пока мы не передадим ей путь «.», «./», «..» или «../». В таком случае basename "$relPath" выдаст «.» или «..», и мы в результате получим путь, который выглядит, соответственно, как-то вроде «/home/user/cat1/cat2/.» или «/home/user/cat1/cat2/..», который работать-то будет, но выглядит не очень.

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

absPath=$(readlink -f $(readlink -f "$(dirname "$relPath")")/$(basename "$relPath"))
УжасноПлохоНормальноХорошоОтлично (1 голосов, средний: 5,00 из 5)
Loading ... Loading ...

Экономим свое время в bash

Хотя даже само по себе использование bash дает нам преимущество в скорости выполнения многих рутинных задач, нужно всегда стремиться к совершенству и выжимать максимум из своих инструментов. Кому-то, возможно, покажется не очень удачной идея тратить свое время сейчас, чтобы потенциально сэкономить его потом. Но в действительности сейчас вы тратите время один раз, чтобы потом много раз его экономить

У меня по этому поводу всплыла в памяти одна цитата:

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

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

Так вот bash (и я вслед за ним) исповедует как раз такую философию: затраченные однажды усилия не должны затрачиваться снова. Ниже я приведу несколько советов, которые в рамках этого подхода позволяют иногда сэкономить несколько секунд. Мелочь, но с миру по нитке…

CDPATH

Переменная окружения CDPATH является аналогом переменной PATH с той разницей, что вторая определяет пути поиска исполняемых файлов, а первая — пути поиска каталогов, задаваемых в команде cd.

Если CDPATH не установлена, то поиск происходит только в текущем каталоге.

Обычно соответствующая строка в ~/.bash_profile выглядит как-то так:

export CDPATH=.:~:~/Dropbox:~/dload:~/wspace

Теперь, например, если в каталоге ~/dload есть подкаталог bibl, то каким бы ни был текущий каталог, команда cd bibl приведет нас в ~/dload/bibl.

Поиск в каталогах, указанных в CDPATH, происходит в том порядке, в котором они указаны в этой переменной. Поэтому обычно первым каталогом в CDPATH указывают текущий (точка), потому что в противном случае поиск в текущем каталоге будет выполняться после всех каталогов из CDPATH, что вряд ли кто-то сочтет разумным.

alias

Команда alias позволяет создать псевдоним — короткое имя для любой команды или их последовательности. Чтобы создать псевдоним, нужно выполнить:

alias name="command"

Где, понятное дело, name — имя псевдонима, а command — что нужно выполнить, когда мы набираем в консоли name. Например:

alias pa="ps -A | grep"

дает нам новую команду pa, с помощью которой удобно искать процессы по части их имени. Обычно все вызовы alias записываются в файле ~/.bashrc.

Команда unalias выполняет обратное действие, то есть уничтожает указанные псевдонимы. Ключ -a предписывает удалить все псевдонимы.

Если выполнить alias без параметров, она выведет список всех определенных псевдонимов, причем в виде команд, которыми они создаются.

Кстати, имя псевдонима вполне может совпадать с именем существующей команды. Более того, это часто используется для задания командам «параметров по умолчанию». Например, у меня есть такой псевдоним:

alias ls="ls --color=auto"

Теперь «команда» ls будет раскрашивать свой вывод, и это не придется указывать явно. Если же мы хотим обратиться к исходной команде (то есть, временно запретить раскрытие псевдонимов), нужно перед ее именем поставить символ «\» (\ls).

Повторение набранного

Существуют специальные комбинации символов, хранящие последнюю введенную команду и ее аргументы:

  • !! — вся команда целиком;
  • !* — все аргументы;
  • !:2 — второй аргумент;
  • !$ — последний аргумент.

Чаще всего нужно обратиться или ко всей команде, или к последнему аргументу. Например, можно создать псевдоним на основе последней команды:

alias foo="!!"

или повторно выполнить команду от имени root:

sudo !!

или что-нибудь сделать с только что созданным файлом:

touch longscriptname.sh
chmod +x !$

В общем, вариантов много, проявите фантазию.

Поиск по истории команд

Это, наверное, и так все знают, но все же. Жмем Ctrl-r, вводим часть команды, затем жмем Ctrl-r до тех пор, пока не отобразится нужная команда. Enter.

На этот раз все. Призываю читателей поделиться своим bash-кунг-фу (по желанию добавлю в пост).

———> Постовой

Теперь все знают, что crystalbit ведёт delphi блог и обсуждает там аспекты программирования.

УжасноПлохоНормальноХорошоОтлично (4 голосов, средний: 4,75 из 5)
Loading ... Loading ...

Перенаправление ввода-вывода в bash

Когда я начинал изучать написание сценариев bash, была такая штука, которую я никак не мог понять — перенаправление ввода-вывода. Снова и снова я перечитывал документацию, дословно переписывал оттуда положенные циферки и амперсанды, но никак не мог понять расстановки этих самых магических символов.

Я предполагаю, что вы знакомы с понятиями «файловый дескриптор», «поток ввода (вывода)», «конвейер» и т.д., но просто никак не можете запомнить, как всем этим пользоваться. На самом деле все просто. Смотрите.

Команда перенаправления вывода позволяет все, что записывается в один файловый дескриптор (откуда), записать вместо этого в другой файловый дескриптор (куда):

откуда > куда

Что может быть на месте откуда:

  • ничего: перенаправление из дескриптора 1 (stdout);
  • i: перенаправление из дескриптора i;
  • &: перенаправляются сразу stdout и stderr.

Что может быть на месте куда:

  • &j: перенаправление в дескриптор j;
  • имя файла: файл открывается в режиме записи, и перенаправление осуществляется в него.

Если используется оператор перенаправления >> (дописать в конец) вместо > (очистить и писать сначала), то куда может быть только именем файла.

Указываемый дескриптор не обязательно должен быть открыт. Номера с 3 по 9 можно использовать как «перевалочные пункты»; открытые файлы получают дескрипторы, начиная с 10.

Перенаправление ввода работает очень похоже:

куда < откуда

Теперь при чтении из куда на самом деле чтение будет происходить из откуда. Тут по сравнению с перенаправлением вывода все наоборот. Куда может быть пустым (stdin) или номером дескриптора. Заметьте, что символ & здесь уже не годится — нельзя читать из двух потоков сразу. Откуда может быть или вида &i, или именем файла, который при этом открывается для чтения.

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

ps -aF > myfile 2>&1

Здесь сначала stdout перенаправляется в файл, а затем stderr перенаправляется в stdout. В результате и stdout, и stderr перенаправляются в файл. Замечу, что перенаправление выполняется по порядку. То есть, такая команда будет работать по-другому:

ps -aF 2>&1 > myfile

Здесь stderr будет выведет на консоль. Правило простое: перенаправление происходит в текущую конечную точку перенаправления дескриптора (если не поняли, прочитайте предложение еще раз).

Еще один тонкий момент: как быть с конвейерами? Распространяется ли действие перенаправления на весь конвейер? Правила просты:

  • перенаправление действует только на ту команду в конвейере, где оно задано;
  • команда выдает в конвейер данные, выведенные в дескриптор 1 (с учетом перенаправлений);
  • команда берет из конвейера данные, полученные из дескриптора 0 (с учетом перенаправлений).

Например, вот так можно перенаправить поток ошибок в конвейер:

ps -aF 3>&1 1>&2 2>&3 | grep something

Здесь stderr и stdout фактически меняются местами.

Конечно, можно много чего еще сказать по поводу перенаправления: про одновременное перенаправление и ввода, и вывода, про перенаправление блока команд, про перенаправление с помощью exec, про закрытие дескрипторов и т.д. Но тогда пост получится слишком объемным, и им неудобно будет пользоваться как справкой. Если у вас есть какие-то вопросы, готов ответить в комментариях. За найденные ошибки также буду признателен.

УжасноПлохоНормальноХорошоОтлично (4 голосов, средний: 5,00 из 5)
Loading ... Loading ...