Порой задалбывает делать с файлами одно и то же много раз, отчего появляется желание эти действия автоматизировать. Сценарии командной строки иногда здорово выручают в таких случаях. Допустим, бывает нужно перелопатить папку с кучей файлов - сконвертировать накопившиеся видео, обработать много картинок (ресайз, водяные знаки и т.д.). Ну или даже с одним файлом сделать что-то такое, что требует километровой команды с парой десятков аргументов.
Примером последней ситуации может служить батник (да, сейчас речь про Windows), который я написал, чтобы с помощью FFmpeg конвертировать видео в анимированную гифку. И вроде бы всё с ним хорошо, но каждый раз сопровождать вызов батника копированием пути к исходному видео тоже со временем надоедает. Можно скормить батнику файл путём перетаскивания (drag-and-drop), но это удобно лишь до тех пор, пока список файлов не перестанет помещаться в окне целиком.
В какой-то момент я подумал, что неплохо было бы вызывать сценарий для конкретного файла прям из контекстного меню Проводника. Некоторые программы ведь как-то умудряются добавлять туда свои пункты. Может, и с батником так получится?
Похожим вопросом, естественно, задавался не я один. Поиск в интернете дал свои плоды - кроме познавательных дискуссий на stackoverflow нашелся еще и мегаподробный туториал (на английском, автор Liam Collod). От него и будем отталкиваться.
Основные принципы
Чтобы настроить контекстное меню Проводника и добавить туда свой пункт, придется лезть в реестр. В принципе, все нужные разделы и параметры можно создать вручную в редакторе реестра (Win+R и написать regedit), но удобнее сделать это с помощью reg-файла. Это позволит применить все нужные настройки разом и упростит откат изменений, если мы захотим что-то поменять.
Раздел, который будем редактировать, зависит от наших целей. Разные типы объектов имеют свои контекстные меню. Допустим, мой сценарий принимает на вход один файл с расширением .avi, но могут быть и другие случаи, когда нужно обработать папку или целый диск. И для каждого случая есть отдельное место в реестре:
Любой файл | HKEY_CURRENT_USER\Software\Classes\*\shell\ |
Файл определенного типа | HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\{ТИП_ФАЙЛА}\shell |
Папки | HKEY_CURRENT_USER\Software\Classes\Directory\shell |
Фон внутри папки | HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell |
Диск | HKEY_CURRENT_USER\Software\Classes\Drive\shell |
Если изменения должны коснуться всех пользователей компьютера, а не только текущего, то копать надо в корневом разделе HKEY_LOCAL_MACHINE вместо HKEY_CURRENT_USER.
Создать reg-файл достаточно просто. По сути это текстовый файл, в котором в первой строке обязательно написано Windows Registry Editor Version 5.00. Дальше в этом файле можно описывать разделы и параметры, которые хотим добавить (или удалить, но об этом позже). Раздел добавляется строкой в квадратных скобках, а параметры описываются строками под соответствующим разделом. В конце остается сохранить получившееся творчество в файл с разрешением .reg и воплотить задуманное в жизнь двойным щелчком по этому файлу.
Для плавного входа в тему начнем с простого примера и попробуем вызвать батник через контекстное меню по правому щелчку на рабочем столе (либо в пустом месте папки). Создадим reg-файл следующего содержания:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Say_Hello]
"MUIVerb"="Hello, computer!"
"Icon"="%SystemRoot%\\System32\\shell32.dll,71"
[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Say_Hello\command]
@="F:\\hello.bat"
Как видите, много для решения этой задачи не потребовалось. Пункт меню создается путем добавления раздела внутри \shell. Можно сказать, что Say_Hello - это внутреннее имя нашего пункта. За отображаемый текст отвечает параметр MUIVerb - пользователь увидит в меню то, что написано в кавычках. Еще он может увидеть иконку, если мы зададим необязательный параметр Icon - он может содержать путь к файлу .dll с иконками (как здесь) или файлу .ico. Для того, чтобы новый пункт меню не просто так красовался, а реально что-то делал, нужно в созданном ранее разделе создать еще один раздел - \command. Его параметр по умолчанию (символ @) - это путь к исполняемому файлу, например, к сценарию hello.bat.
Обратите внимание, что там, где мы хотели бы написать один слэш \, приходится ставить двойной \\. Если посреди строки захочется поставить кавычку, ее так же нужно заменять на \". В данном простом примере это не проблема, но в некоторых случаях (рас)шифровка может превратиться в головоломку.
Чтобы довести пример до конца, создадим .bat файл по указанному в reg-файле пути. Пусть это будет что-то простое, вроде такого:
@echo off
echo Hello, human!
pause
Теперь щелкнем правой кнопкой в свободном месте рабочего стола папки и увидим в меню наш новый пункт.
Впрочем, это был всего лишь тест. Мало кому требуется каждый день быстро и эффективно здороваться с компьютером, поэтому последствия эксперимента необходимо откатить. Удалить добавленные разделы вместе с их параметрами можно reg-файлом с почти аналогичным содержимым. Вся разница будет заключаться в том, что перед строками с путями разделов добавляется дефис (минус).
Windows Registry Editor Version 5.00
[-HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Say_Hello]
"MUIVerb"="Hello, computer!"
"Icon"="%SystemRoot%\\System32\\shell32.dll,71"
[-HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Say_Hello\command]
@="F:\\hello.bat"
Если нужно удалить отдельный параметр, но не раздел целиком, тогда минус ставят справа от знака равенства для соответствующего параметра. Однако сегодня нам это не понадобится.
Вложенные меню и действия с файлом
Самое время вернуться к изначальной задаче - конвертации видео в gif-файл, и посмотреть, что у меня в конечном итоге получилось. Путь раздела будет другой, что ожидаемо - в этот раз изменения вносим в меню файла .avi, а не фона папки. Но есть еще пара интересных моментов.
Если вызывать исполняемый файл без аргументов, он не поймет, какой именно файл ему нужно обработать. При вызове меню для файла полный путь к нему будет содержаться в переменной %1 - просто добавьте её в нужное место в строке команды.
В будущем могут понадобиться дополнительные пункты меню, но при этом со схожей тематикой. Чтобы не делать огромную простыню из основного меню, мы можем сгруппировать несколько пунктов и получить вложенное меню (sub-menu). Для этого в разделе "родительского" пункта добавим параметр subCommands. Затем для каждого пункта вложенного меню создадим разделы, путь к которым повторяет путь "родителя", но при этом в конце добавим \shell\{имя_нового_пункта}. Ну а если вдруг понадобится изобрести матрешку и сделать многоступенчатое вложенное меню, то просто действуйте по аналогии - копируйте путь точки ветвления и так же дописывайте \shell\{имя_нового_пункта}.
Не забываем добавлять подраздел \command для каждого пункта, который должнен выполнять какое-то действие.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.avi\shell\FFmpeg_Scripts]
"MUIVerb"="FFmpeg scripts"
"subCommands"=""
[HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.avi\shell\FFmpeg_Scripts\shell\video-to-gif]
"MUIVerb"="convert to .gif"
[HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.avi\shell\FFmpeg_Scripts\shell\video-to-gif\command]
@="\"F:\\Scripts\\ffmpeg_video2gif.bat\" \"%1\""
Строка вызова команды, кстати, превратилась в пусть и не сложную, но все же головоломку из знаков препинания. Причина - возможные пробелы в путях. В командной строке аргументы отделяются друг от друга пробелом, и если аргумент с пробелами не заключить в кавычки, то он будет воспринят как несколько, весь порядок развалится и работать команда не будет. Ну а поскольку значение параметра @ итак уже задается строкой в кавычках, приходится заниматься экранированием символов - заменять внутренние кавычки и слэши на те самые \" и \\.
И еще - сам пока не проверял, но согласно упомянутому в начале туториалу, пункты вложенного меню сортируются по внутренним именам (имя раздела после \shell) в алфавитном порядке. Если по осмысленным названиям функций нужный порядок не складывается, можно попробовать добавить цифр в начале имени, например 01video-to-gif вместо video-to-gif.
Нюансы
Самое мясо теперь позади, осталась пара замечаний напоследок.
Раз переменная %1 содержит путь к выделенному файлу, напрашивается вопрос - а что если выделить несколько файлов? Как передать список этих файлов батнику? А никак не передать (разве что каким-то извращенным непрактичным способом). Команда выполняется один раз отдельно для каждого файла, и если я выделю 10 avi-файлов и захочу их все обработать, сценарий запустится 10 раз и откроется одновременно 10 окон.
В туториале, упомянутом в начале поста, Лиам часто вызывает сценарии не напрямую, а через cmd /k. Мотивирует он это тем, что хочет видеть в консоли результат выполнения команды, а без параметра /k окно закроется сразу по завершении работы.
@="cmd /k \"\"D:\\resources\\maketx.exe\" \"%1\"\""
У меня такой способ заработал не с первой попытки - сначала вместо батника просто запускалась голая командная строка. Видимо, я где-то допускал опечатку, что немудрено - здесь строка с кавычками, заключенная в кавычки, заворачивается еще в одни кавычки. Впрочем, когда нет настроения раскладывать кавычковый пасьянс, есть другое решение - добавить в конце своего батника команду pause, как мы уже делали сегодня в примере hello.bat. С этой командой выполнение сценария приостанавливается в ожидании ввода с клавиатуры. Правда, есть подвох - если вы решите вызывать такой батник в цикле из другого батника, придется много жать на кнопки :) Так что pause - не панацея, и если ваш сценарий не является интерактивным, то запускать его через cmd /k будет разумнее.
Сообщение может быть отклонено, если содержит спам, противозаконный контент, а так же оскорбления и грубость по отношению к другим участникам обсуждения.