Статьи

Как писать сценарии оболочки с помощью JavaScript

«Как писать сценарии оболочки с помощью JavaScript» — это редакционная статья нашего последнего бюллетеня JavaScript, вы можете подписаться здесь .

На этой неделе мне пришлось обновить веб-сайт клиента, чтобы использовать SSL. Само по себе это не было сложной задачей — установка сертификата была простым нажатием кнопки — но как только я сделал это, у меня осталось много предупреждений о смешанном контенте . Частично их исправление означало, что мне пришлось пройти по каталогу тем (это был сайт WordPress) и определить все файлы, в которые были включены ресурсы через HTTP.

Раньше я бы использовал небольшой скрипт Ruby для автоматизации этого. Ruby был первым языком программирования, который я выучил, и идеально подходит для таких задач. Однако недавно мы опубликовали статью об использовании Node для создания интерфейса командной строки . Эта статья напомнила мне, что JavaScript давно вышел за рамки браузера и может (среди многих других вещей) быть полезным для сценариев рабочего стола.

В оставшейся части этого поста я объясню, как использовать JavaScript для рекурсивной итерации по файлам в каталоге и для выявления любых вхождений указанной строки. Я также предложу небольшое введение в написание сценариев оболочки на JavaScript и помогу вам написать свой собственный.

Настроить

Единственной предпосылкой здесь является Node.js. Если у вас это еще не установлено, вы можете зайти на их сайт и скачать один из бинарных файлов . Кроме того, вы можете использовать менеджер версий, например, nvm. У нас есть учебник по этому вопросу здесь .

Ваш первый сценарий оболочки

Так с чего начать? Первое, что нам нужно сделать, это перебрать все файлы в каталоге темы. К счастью, родной модуль файловой системы Node поставляется с методом readdir, который мы можем использовать для этого. Он принимает путь к каталогу и функцию обратного вызова в качестве параметров. Обратный вызов получает два аргумента ( errentriesentriesentries. и ..

 const fs = require('fs');

function buildTree(startPath) {
  fs.readdir(startPath, (err, entries) => {
    console.log(entries);
  });
}

buildTree('/home/jim/Desktop/theme');

Если вы придерживаетесь этого, сохраните вышесказанное в файле с именем search_and_replace.jsnode search_and_replace.js Вам также необходимо настроить путь к любому каталогу, который вы используете.

Добавление рекурсии

Все идет нормально! Вышеприведенный скрипт записывает записи верхнего уровня каталога в консоль, но папка моей темы содержала подкаталоги, в которых также были файлы, требующие обработки. Это означает, что нам нужно перебрать массив записей и иметь сам вызов функции для любых каталогов, с которыми он сталкивается.

Для этого нам сначала нужно разобраться, имеем ли мы дело с каталогом. К счастью, в модуле файловой системы тоже есть метод для этого: lstatSync . Это возвращает объект fs.Stats , который сам имеет метод isDirectory Этот метод возвращает truefalse

Обратите внимание, что мы используем синхронную версию lstat Это хорошо для одноразового скрипта, но асинхронная версия должна быть предпочтительной, если производительность имеет значение.

 const fs = require('fs');

function buildTree(startPath) {
  fs.readdir(startPath, (err, entries) => {
    console.log(entries);
    entries.forEach((file) => {
      const path = `${startPath}/${file}`;

      if (fs.lstatSync(path).isDirectory()) {
        buildTree(path);
      }
    });
  });
}

buildTree('/home/jim/Desktop/theme');

Если вы запустите сценарий, вы увидите, что он печатает список файлов и папок для текущего каталога и каждого подкаталога, который он содержит. Успех!

Определение файлов для обработки

Затем нам нужно добавить некоторую логику, чтобы идентифицировать любые файлы PHP, открывать их и искать в них любые вхождения искомой строки. Это можно сделать, используя простое регулярное выражение для проверки имен файлов, заканчивающихся на «.php», затем вызывая функцию processFile

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

 const fs = require('fs');
const Path = require('path');

function processFile(path) {
  console.log(path);
}

function buildTree(startPath) {
  fs.readdir(startPath, (err, entries) => {
    entries.forEach((file) => {
      const path = Path.join(startPath, file);

      if (fs.lstatSync(path).isDirectory()) {
        buildTree(path);
      } else if (file.match(/\.php$/)) {
        processFile(path);
      }
    });
  });
}

buildTree('/home/jim/Desktop/theme');

Если вы запустите скрипт на этом этапе, он должен восстановить дерево каталогов и распечатать путь к любым php-файлам, которые он может найти.

Поиск текста в файле

Осталось только открыть файлы, найденные сценарием, и обработать их. Это можно сделать с помощью метода Node readFileSync, который принимает путь к файлу и его кодировку (необязательно) в качестве параметров. Если кодировка указана, то эта функция возвращает строку. В противном случае он возвращает буфер.

Теперь мы можем прочитать содержимое файла в переменную, которую затем можно разделить на каждый символ новой строки и выполнить итерацию по полученному массиву. После этого просто воспользуйтесь методом соответствия JavaScript для поиска нужного слова или фразы:

 function processFile(path) {
  const text = fs.readFileSync(path, 'utf8');
  text.split(/\r?\n/).forEach((line) => {
    if (line.match('http:\/\/')) {
      console.log(line.replace(/^\s+/, ''));
      console.log(`${path}\n`);
    }
  });
}

Если вы запустите скрипт сейчас, он напечатает каждую строку, в которой найдет совпадение, а также имя файла.

Продолжая

В моем конкретном случае этого было достаточно. Сценарий выплюнул несколько вхождений «http», которые мне удалось исправить вручную. Работа сделана! Однако было бы просто автоматизировать процесс, используя replace()fs.writeFileSync, чтобы изменять каждое вхождение и записывать новое содержимое обратно в файл. Вы также можете использовать child_process.exec, чтобы открыть файлы в Sublime, готовые для редактирования:

 const exec = require('child_process').exec;
...
exec(`subl ${path}`)

Этот вид сценариев позволяет выполнять целую кучу задач, а не только манипулировать текстовыми файлами. Например, может быть, вы хотите переименовать пакет музыкальных треков или удалить каждый файл Thumbs.db Возможно, вы хотите извлечь данные из удаленного API, проанализировать файл CSV или сгенерировать файлы на лету. Список можно продолжить…

Вы также можете сделать исполняемые файлы JavaScript так, чтобы они запускались при нажатии на них. Аксель Раушмайер рассказывает об этом в своем посте. Пишите свои сценарии оболочки на JavaScript через Node.js.

Вывод

И там у нас это есть. Я продемонстрировал, как использовать JavaScript для рекурсии через дерево каталогов и манипулировать подмножеством файлов, содержащихся в нем. Это простой пример, но он служит для того, чтобы подчеркнуть тот факт, что JavaScript может использоваться для целого ряда задач вне браузера, в том числе для настольных сценариев.

Теперь это вам. Вы автоматизируете задачи сценариев с помощью JavaScript? Если нет, у вас есть другой предпочитаемый язык, или вы пушист? Какие задачи вы автоматизируете? Позвольте мне знать в комментариях ниже.