Позднее Ctrl + ↑

Склонировать структуру папок и файлов

Мне нужно было скопировать структуру папок и файлов с одного диска на другой. Чтобы всё лежало так же по папкам, под теми же именами, но каждый файл весил 0 байт.

Смысл этой операции вот в чём. Изредка мне нужно найти что-нибудь среди файлов десятилетней давности. У меня скопилось несколько жёстких дисков с разными архивами. И есть специальная коробочка, в которую можно вставить жёсткий диск и подключить по УСБ. Меня бесит, что я порой с третьего раза нахожу, на каком из них лежит то, что мне нужно.

Я придумал сделать папки, в которых будет лежать полная файловая структура каждого из моих жёстких дисков без самих данных. Тогда я смогу мгновенно находить любой файл по имени спотлайтом, а уже потом сразу втыкать жёсткий диск с настоящим файлом.

Пришлось попотеть, чтобы понять, как это сделать. У меня получилось так:

cd папка_которую_клонируем
find . -type d -print0 | xargs -0 -I {} mkdir -p папка_куда_клонируем/{}
find . -type f -print0 | xargs -0 -I {} touch папка_куда_клонируем/{}

Сложность была в том, что find возвращает список найденных файлов с пробелами в именах, и когда такие файлы передаёшь куда-то ещё, всё взрывается. Пришлось просить find в конце имени каждого найденного файла ставить символ 0 (-print0), а потом с помощью адской команды xargs это разбирать и скармливать в качестве аргументов другим командам. Учитывая, что я раньше ничего сложнее apachectl restart не писал, это была жесть.

Добавлено через полчаса: Читатели рассказали мне про -exec

Аудио по четвергам: «Индиго»

Это отличный трек Джерома Исмы-Эя «Индиго» (2005):

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

Как я узнал, что domenica — это воскресенье

Люблю случайно изучать языки. В данном случае я даже не сразу узнал, о каком языке речь. Сначала почему-то подумал, что об испанском, но потом дошло, что об итальянском.

Так вот. Есть такая дурацкая, но клёвая песня:

Самый кайф после 2:30, но сейчас нас интересует не это. В 1:13 там начинается какой-то текст про дискотеку. Мне в общем-то пофиг на текст, но когда я на третий раз услышал «Domenica... a la discoteca» у меня щёлкнуло: «А, так domenica — это воскресенье? Это ж почти как dimanche по-французски».

И тут я понял, о чём эта песня. Песня-разгадка (ещё клёвее):

«Lunedi sera, la discoteca» — ну блин, мог бы и с первого раза понять, учитывая, что lundi — это понедельник по-французски, а buonasera — добрый вечер по-итальянски.

Слова weight и height

Часто веб-разработчиков ставит в лингвистический тупик такой ЦСС-код:

font-weight: normal;
line-height: 1.4em;

Запомните: слово height читается «хайт» несмотря на то, что слово weight читается «вейт».

Как работает мой сайт

Мой сайт работает на моём самописном движке на ПХП (единственное исключение — раздел «Блог», о нём в конце).

Файлы страниц сайта лежат в папках, соответствующих урлам разделов, например страница про Ангстрем лежит на сервере под именем .../www/projects/angstrom/angstrom.php. Задача этого файла — сгенерить ХТМЛ смысловой части этой страницы.

Метаданные

Рядом с файлом имя-папки.php должен лежать файл метаданных _имя-папки.php. Для Ангстрема он выглядит примерно так (выкинул часть полей для простоты):

<?php return array (

  'logo' => 'angstrom',
  'value' => '20140201',
  'title' => 'Ангстрем', 

  'x-showcase-title' => 'Ангстрем, конвертер всего',
  'x-copyright-years' => '2014...',

  'languages' => array (
    'english' => array (
      'title' => 'Ångström',
      'x-showcase-title' => 'Ångström, the converter',
    ),
  ),

) ?>

Задача движка — понять, на какой из моих многочисленных доменов и по какому адресу зашёл пользователь, определиться с языком, найти файл нужной страницы, выполнить его, пропустить через шаблонизатор и типограф и отдать наружу.

Там умная и гибкая система, которая позволяет делать отличия между сайтами на разных языках (ilyabirman.ru и ilyabirman.net) только там, где они нужны: домен, текст, правила типографики, твиттер-акаунт для шаринга; но всё остальное хранить в одном месте один раз.

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

Вот примерный код, который её рисует (подсветка синтаксиса глючит):

<?php $showcase_elements = array (
  'angstrom',
  'moscow-metro-multiplication',
  'therules-2-for-ios',
  'chelyabinsk-trams-2015',
  'emcee',
  'train-thirteen',
  'wireless-dj',
  'snooker-results-display',
  'moscow-metro-poster-vdnh',
  'forebruary',
  'moscow-metro-nanomap',
); ?>

<?php foreach ($showcase_elements as $showcase_element) { ?>
  <div class="b-showcase-element" style="width: 160px">
    <div class="b-showcase-element-image-wrapper">
      <div>
        <a href="<?= HREF ($showcase_element) ?>" class="nu">
          <img
            src="<?= FOLDER ($showcase_element) ?>i/icon-160@2x.png"
            alt="<?= strip_tags (TITLE ($showcase_element)) ?>"
            width="160" height="160"
          />
        </a>
      </div>
    </div>
    <div class="b-showcase-element-title">
      <a href="<?= HREF ($showcase_element) ?>">
        <?= EX ('showcase-title', $showcase_element)?>
      </a>
    </div>
  </div>
<?php } ?>

Макросы

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

  • HREF ($logo) возвращает полный урл страницы по её идентификатору;
  • FOLDER ($logo) возвращает путь к папке, в которой лежит страница;
  • TITLE ($logo) возвращает название страницы;
  • EX ($what, $logo) возвращает значение поля x-something из файла метаданных.

Я, как видите, использую EX и поле x-showcase-title для того, чтобы показывать особые заголовки в «витрине». Если я вдруг решу переименовать один из проектов и перенести в другую папку, и ещё сделать про него страницу на французском, чтобы она была доступна по адресу ilyabirman.net/french/обычный-путь-к-проекту — это займёт пару минут, не считая времени написания французского текста. При этому создавать папку /french/ на сервере и копировать туда все файлы не придётся — языковые элементы урла обрабатываются отдельно и на структуру папок на сервере не влияют.

Есть и другие макросы. CHILDREN вернёт массив всех дочерних для данной страниц. Например, в разделе проектов их список автоматически дополнится новым, если я создам подпапку со страницей нового проекта в ней.

А ещё у меня на страницах бывают переключалки между связанными страницами, вот, например, такой переключалкой провязаны несколько рассказов про Лондон. Это работает, потому что эти страницы связаны в одну группу через поле group в файле метаданных. Они при этом могут лежать вообще по любым урлам, их просто вернёт GROUPLINGS. Если одна из страниц группы окажется недоступна на одном из языков — не беда, значит не попадёт в список. Движок не станет генерить мёртвые ссылки.

Или вот есть ещё WITHIN, определяется так:

function WITHIN ($what, $id = false) {
  return AT ($what) or INSIDE ($what, $id);
}

Удобно использовать в меню, например, в котором плашку у текущего раздела надо поставить если мы в нём или любых его подразделах.

Есть макрос LANG, который возвращает текущий язык. Его удобно использовать, если страницы на разных языках отличаются двумя-тремя строками текста. Но чтобы не писать сто раз if (LANG == ’russian’) { ... }, можно просто положить в папке два файла — angstrom-english.php и angstrom-russian.php, тогда движок сразу возьмёт нужный из них.

Ещё есть TAIL, OFFSET, SIBLINGS, NEIGHBOURS и всякие другие, которые позволяют компактно и внятно выражаться в коде.

Я это называю макросами, а не функциями, потому что это точнее передаёт смысл, и ещё их можно вызывать без параметра $logo и без скобок (если других параметров нет), и тогда они сработают для текущей собираемой страницы:

<h1><?= TITLE ?></h1>

Шаблоны

У любой страницы в файле метаданных может быть поле apply-template — оно говорит движку, что надо пропустить страницу через определённый шаблон перед тем, как заворачивать в обёртку из меню и подвала.

Например, в подвале любого рассказа о поездке я даю ссылки на соседние. Их сколько нужно возвращает макрос SIBLINGS. Но я не копирую этот этаж из рассказа в рассказ — для этого у меня есть шаблон world-story, который применяется ко всем рассказам из поездок.

Шаблон может не просто завернуть страницу во что-то, но и как угодно её обработать перед использованием.

Вот начало файла .../www/world/london-2011-may/london-2011-may-russian.php:

Лондон — лучший город, где мне доводилось бывать.
IMG_0333.jpg Парламент и Биг-бен в Лондоне

Достопримечательностям здесь совершенно необязательно быть в поле зрения, чтобы было понятно, где ты находишься. Каждая деталь напоминает об этом:
IMG_0238.jpg Телефонная будка, почтовая машина и автобус в Лондоне

Похоже на текст из редактора Эгеи, правда? Потому что я тут использую Нисден — форматтер Эгеи. Шаблон world-story автоматически пропускает текст через него. Поэтому тут автоматически работает всякая резиновость картинок, фоторамы и всё остальное, и в результате сам собой получается рассказ про Лондон.

Блог

Теперь про блог. Блог работает на Эгее и живёт своей жизнью, несмотря на внешнее сходство с остальным сайтом. Эгея, в отличие от остального сайта, использует базу данных для хранения заметок, комментариев и прочего. Сайту никак не мешает, что в папке /meanwhile/ у него живёт что-то «неродное». Он не находит там файла _meanwhile.php и поэтому считает, что эта папка для него не представляет ценности.

Мне надо, чтобы в блоге у меня использовался ЦСС с основного сайта, но всё же в нём есть несколько своих особенностей, поэтому напрямую я не могу залинковать тот же самый файл. Чтобы не носить общие для сайта изменения из одного файла в другой, я использую Галп, который автоматически собирает нужную версии ЦСС-файла для блога.

Ещё, как вы знаете, у меня есть русский и английский блоги. На самом деле их выдаёт одна и та же копия Эгеи, которая в зависимости от домена использует разные таблицы в базе данных.

Это недокументированная функция, но по секрету скажу что там, где у вас в Эгее лежит папка /user/, у меня лежит папка /users/ с двумя подпапками. Да, Эгея умеет быть многопользовательской уже много лет! Только никому не говорите. Если серьёзно, это работает на костылях. Заранее говорю, что не буду отвечать на письма с вопросом «как мне сделать так же».

Люди используют пробел для прокрутки страниц

Издревле в браузерах пробел прокручивает страницу на один экран вниз.

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

Cреди моей аудитории больше трети людей используют пробел

Даже среди моей продвинутой аудитории больше трети людей используют пробел!

Некоторые музыкальные и видеосайты используют пробел для управления воспроизведением, и на мой взгляд это намного более рациональное использование кнопки. Когда это не работает, сайт кажется неуправляемым. В Ютюбе пробел почему-то работает через раз — иногда вместо того, чтобы остановить или запустить видео, он проматывает страницу к комментариям, которые мне до звезды. А в видеоразделе для разработчиков Эпла пробел не работает вообще никогда, несмотря на то, что на странице с видосом вообще ничего полезного кроме этого видоса нет.

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

Сто лет одиночества: либералы, консерваторы и честные выборы

Как прошли выборы в городке Макондо:

Либералы готовились развязать войну. Поскольку в ту пору Аурелиано имел весьма туманное представление о консерваторах и либералах, тесть простыми словами изложил ему, в чём состоит разница между этими партиями. Либералы, говорил он, — это фасоны, скверные люди, они стоят за то, чтобы отправить священников на виселицу, ввести гражданский брак и развод, признать равенство прав законнорожденных и незаконнорожденных детей и, низложив верховное правительство, раздробить страну — объявить её федерацией. В противоположность им консерваторы — это те, кто получил бразды правления непосредственно от самого Господа Бога, кто ратует за устойчивый общественный порядок и семейную мораль, защищает Христа, основы власти и не хочет допустить, чтобы страна была раскромсана. Из чувства человечности Аурелиано симпатизировал либералам во всем, что касалось прав незаконнорожденных детей, но не мог понять, зачем нужно впадать в крайности и развязывать войну из-за чего-то такого, что нельзя потрогать руками. Ему показалось чрезмерным усердие тестя, затребовавшего на время выборов в лишенных всяких политических страстей городок шесть вооруженных винтовками солдат с сержантом во главе. Солдаты не только прибыли, но обошли все дома и конфисковали охотничьи ружья, мачете и даже кухонные ножи, а затем раздали мужчинам старше двадцати одного года голубые листки с именами кандидатов консерваторов и розовые — с именами кандидатов либералов. В субботу, накануне выборов, дон Аполинар Москоте лично огласил декрет, запрещавший, начиная с полуночи и в течение сорока восьми часов, торговать спиртными напитками и собираться группами числом более трех человек, если это не члены одной семьи. Выборы прошли спокойно. В воскресенье, в восемь часов утра, на площади была установлена деревянная урна под охраной шести солдат. Голосование было совершенно свободным, в чем Аурелиано мог убедиться сам — почти весь день он простоял рядом с тестем, следя, чтобы никто не проголосовал больше одного раза. В четыре часа дня барабанная дробь возвестила о конце голосования, и дон Аполинар Москоте опечатал урну ярлыком со своей подписью. Вечером, сидя за партией в домино с Аурелиано, он приказал сержанту сорвать ярлык и подсчитать голоса. Розовых бумажек было почти столько же, сколько голубых, но сержант оставил только десять розовых и пополнил недостачу голубыми. Потом урну опечатали новым ярлыком, а на следующий день чуть свет отвезли в главный город провинции.

«Либералы начнут войну», — сказал Аурелиано. Дон Аполинар даже не поднял взгляда от своих фишек. «Если ты думаешь, что из-за подмены бюллетеней, то нет, — возразил он. — Ведь немного розовых в урне осталось, чтобы они не смогли жаловаться». Аурелиано уяснил себе все невыгоды положения оппозиции. «Если бы я бы либералом, — заметил он, — я бы начал войну из-за этой истории с бумажками». Тесть поглядел на него поверх очков.

Габриэль Гарсиа Маркес. Сто лет одиночества

Дорогая редакция: стиральная и посудомочная машины читателей

Заметка про кнопки включения-выключения вызвала удивительно количество откликов.

Илья Синельников показывает свою стиральную машину и сушилку:

Пишет:

Минуту соображал, почему машина не работает, когда выбрал программу и нажимаешь «Старт». А ещё, чтобы включить, кнопку надо держать 2 секунды. Прикинь?

Какой же бред, учитывая, что режим выбирается физической крутилкой безо всяких светодиодов.

Глеб Крауклиш показывает свою посудомоечную машину:

Дорогая редакция: стиральная и посудомочная машины

Пишет:

Прочитал сегодняшний пост с ответом читателя и решил показать свою посудомоечную машину. В ней всего одна кнопка, «вкл. — выкл.» Когда машина включена, она начинает мыть посуду (с задержкой секунд в пять, чтоб успеть поменять настройки или поставить отсрочку). Когда выключена, ничего не делает.

До этой у меня были три машины с двойными кнопками. Я даже не думал, что бывает по-другому, и новая машина мне сначала не понравилась. Но как только попользовался, понял, что так и надо.

Ну, вообще, это тоже не очень. Что за пять секунд? Почему именно пять, а не три или не семь? Временны́е задержки в интерфейсе — зло, про это надо будет тоже заметку написать.

Ранее Ctrl + ↓