. Немного предпятничных задачек на Bash
Шрифт
Немного предпятничных задачек на Bash
Play

Немного предпятничных задачек на Bash

Какую одну команду нужно выполнить, чтобы следующая команда из примера вывела Hello на ваш терминал?

Для стандартных потоков (STDIN, STDOUT, STDERR) каждого процесса, автоматически создаются файловые дескрипторы (0, 1, 2). Мы заходим в подкаталог на procfs (/proc), подкаталог нашего процесса определяем через /proc/$$ (специальная переменная, в которой хранится PID текущего процесса), и наконец в подкаталог с дескрипторами "/proc/$$/fd". Дескрипторы тут так и лежат 0(stdin), 1(stdout), 2(stderr). С ними можно работать как с обычными символьными устройствами. Тут же будут создаваться дескрипторы других файлов, которые открыты в указанном процессе.

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

Именно через этот механизм работает популярная утилита write — когда пользователь может написать другому пользователю сообщение без запуска какого-то мессенджера — просто в его терминал. А для того, чтобы write могла писать в дескриптор другого пользователя, на бинарнике write стоит флаг SGID (пользователи должны быть добавлены в группу tty). Через этот же механизм система оповещает подключенных пользователей о ребутах и других системных алертах.

2. Не столько задачка, сколько вопрос-напоминание.

Что выведет следующая команда?

Выдаст ошибку? Выведет первый попавшийся файл? Выведет все файлы? А куда мы зайдем следующей командой:

Какой результат последней команды:

Уверен, что все ответили верно: Команда cat выведет все файлы, обойдя все подходящие по шаблону директории. cd зайдет в первую, подошедшую под шаблон директорию. Обходить она не будет, просто подберет первое по алфавиту. cp скопирует первый подошедший по шаблону файл в текущую директорию, а на остальные будет ругаться с ошибкой, потому что cp не может перезаписать в тот же самый destination в пределах выполнения одного экземпляра. На всякий случай — а что будет, если сделать:

3. А вот это действительно забавная задачка!

Даже хотел ее кинуть первой, но решил оставить на закуску. Итак ситуация такая:

Теперь выведем их через "ls -1" и простой регуляркой отфильтруем первые пять:

В результате пусто? Что за? где мои файлы?

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

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

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

Написанное выше — общеизвестно, но вот не все знают, что *nix также поддерживает в wildcards перечисление символов в виде [abc].

В нашем случае шелл «раскрыл» маску и передал в grep длинную строку, попытавшись выполнить команду «ls -1 | grep file1 file2 file3 file4 file5». В этом случае grep будет искать строку file1 в файлах file2, file3, file4, file5, но так как файлы пустые, то он ничего не вернет (спасибо mickvav за уточнение).

Если выполнить команду, содержащую wildcard в каталоге, где нет подходящих файлов, она не изменится и мы получим как в предыдущем примере с '*':

Кстати частенько даже со старыми знакомыми масками многие новички совершают ошибку, например при выполнении команды find, и получают что-то вроде:

Вывод: Используйте кавычки!

Перечисление символов в wildcard поддерживает и диапазоны и инвертирование. Примеры:

4. Какой простой способ отрезать расширение у файла?

Стандартный и популярный способ — использовать утилиту basename, который отрезает весь путь слева, а если указать дополнительный параметр, то дополнительно отрежет справа и суффикс. Например пишем file.txt и суффикс .txt

Но можно не запускать целый отдельный процесс для такого простого действия, и обойтись внутренними преобразованиями в bash (bash variable expansions):

Или наоборот, отрезать имя файла и оставить только расширение:

% — отрезает все символы с конца до первого подходящего паттерна (поиск идет справа налево) %% — отрезает все символы с конца до последнего подходящего паттерна (справа налево) # — отрезает с начала до первого подходящего паттерна (поиск идет слева направо) ## — отрезает с начала до последнего подходящего паттерна (слева направо)

Таким образом, "$ " означает — начиная справа налево проходим все символы (*) и доходим до первой точки. Отрезаем найденное. Если бы мы использовали "$

📎📎📎📎📎📎📎📎📎📎