Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

99 бутылок: mkdir + find

77. mkdir + find — тут некий японец придумал как писать программы, используя только утилиты mkdir и find. Мне такое очень нравится, поэтому и я решил что-нибудь написать на связке этих утилит. Сразу вспомнил, что давно у меня не было «песни о пиве». Есть у меня такое развлечение — писать её на всём подряд.

Программу вы можете видеть на экране, а как она работает расскажу чуть ниже.

# Written by Evgeny Stepanischev https://bolknote.ru

rm -rf bottles; mkdir bottles
find bottles -maxdepth 98 -execdir mkdir bottles/bottles \;
find bottles -empty -name bottles -execdir mkdir bottles/bottle \;

find bottles \
        -empty -printf "1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.\n" -o \
        -regex '.*/bottles/bottles' \
        -execdir find . \
                -empty -printf "%d bottles of beer on the wall, %d bottles of beer.\n" \; \
        -execdir find bottles \
                -empty -printf "Take one down and pass it around, %d %f of beer on the wall.\n\n" \;

# Clean up
rm -rf bottles

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

Команда mkdir, наверное, многим знакома — это создание директория, а find — утилита для поиска файлов и директориев по различным критериям. Причём с найденным она может делать некоторые простые действия.

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

Во-первых, создание вложенных директориев командой find с параметром -execdir mkdir имя.

Параметр запускает указанную команду в директории, который сейчас просматривает find. Эти вложенные директории в дальнейшем используются как конечный цикл для следующей команды find. Вложенность при этом можно регулировать параметром -maxdepth.

Во-вторых, параметр -printf поддерживает аргумент %d, который позволяет вывести текущий уровень вложенности и полезен в тех же циклах.

В-третьих, фильтры команды find, а так же директивы -o («ИЛИ») и -and («И») можно использовать для организации условий.

Мне пришлось дополнительно решить ещё две проблемы.

Во-первых, цикл у меня обратный. Чтобы его получить, я запускаю две команды find — одну в другой. Первая считает директории в одну сторону по возрастанию, вторая запускается на каждом шаге и итерируется по остатку директориев до конца. Поскольку это тоже цикл, а мне нужно только последнее значение, я использую параметр -empty, чтобы получить только последний директорий — тот, в котором пусто.

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

Во-вторых, мне нужно поддержать окончание множественного числа. Тут я немного смухлевал и слегка замёл эту проблему в конец — там я вывожу дополнительные строки «1 bottle of beer on the wall, 1 bottle of beer. / Take one down and pass it around, no bottles of beer on the wall».

Но в строке перед этим текстом у меня сделано более честно. Как видно, структура директориев у меня такая: 98 раз созданы вложенные bottles, а конечная директория называется bottle.

Это мне позволяет в одном из «циклов», огранизованных через find, использовать аргумент %f параметра -printf, который выводит имя текущего директория. Таким образом в самом конце я получаю bottle вместо bottles.

Добавлено: мне подсказали, что обратный цикл можно огранизовать проще, через ключ -depth, переделал программу под него:

# Written by Evgeny Stepanischev https://bolknote.ru

rm -rf start; mkdir -p start/bottle/bottles
find start/bottle -maxdepth 97 -name bottles -execdir mkdir bottles/bottles \;

find start -mindepth 1 -depth \
        -empty -printf "%d %f of beer on the wall, %d %f of beer.\n" -o \
        ! -empty -printf "Take one down and pass it around, %d %f of beer on the wall.\n\n" \
        -printf "%d %f of beer on the wall, %d %f of beer.\n" \
        -name bottle -printf "Take one down and pass it around, no bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.\n"

rm -rf start
2 комментария
Павел 4 мес

Как ни странно, больше всего в посте удивило словообразование. Колдунство с find я уже пару раз похожее встречал, а вот слово «директориев» — нет. :-)

Евгений Степанищев 4 мес

Я так и не определился с тем какого рода это слово) Слышал оба варианта, употребляю тоже оба, выбираю без системы. Тут почему попал под руку мужской род )

Михаил 4 мес

Попробуйте сделать на Folders — https://danieltemkin.com/Esolangs/Folders/

Евгений Степанищев 4 мес

О, боже! Надо посмотреть, спасибо )))