【问题标题】:Rename files and folders recursively based on regex基于正则表达式递归重命名文件和文件夹
【发布时间】:2017-12-01 18:50:57
【问题描述】:

我有这个文件结构:

FolderName/
    [NAME]/
        [NAME].controller.js
        [NAME].html

使用 Node.js 我想用变量替换 [NAME]。

这是我尝试过的:

const shell = require("shelljs");

shell.ls('-Rl', '.').forEach(entry => {
  if (entry.name.includes(`[NAME]`)) {
    let newName = entry.name.replace(/\[NAME\]/, "Test");
    shell.mv(entry.name, newName);
  }
});

这只会将文件夹 [NAME] 重命名为 Test,而不会更改文件。并输出:

mv: no such file or directory: FolderName/[NAME]/[NAME].controller.js
mv: no such file or directory: FolderName/[NAME]/[NAME].html

【问题讨论】:

    标签: node.js regex shelljs


    【解决方案1】:

    问题

    当在您的示例上下文中运行 shell.mv(entry.name, newName); 时,它会尝试移动/更改不再存在的路径,因为它在循环的前一回合中已更改。这会导致错误:

    mv: no such file or directory: FolderName/[NAME]/[NAME].controller.js

    mv: no such file or directory: FolderName/[NAME]/[NAME].html


    解决方案 A

    为避免错误,请尝试以下方法:

    1. 使用shelljs find 命令获取路径,而不是ls。这将确保生成的路径包含基本目录。
    2. 迭代每条路径和filter 找出任何资产不包含要查找的字符串的路径(例如[NAME]。同时排除任何隐藏资产(即那些以点. 开头的名称)
    3. 按路径深度降序对路径数组进行排序。
    4. 仅用每个路径中的替换字符串(例如TEST)替换要查找的字符串的最后一个实例(例如[NAME])。然后最后使用shelljs mv 命令应用新路径。

    注意:作为保障措施,(在第 4 步),只有在尚未采用新的结果路径时才重命名资产/路径。如果新路径已存在,则报告不应重命名的路径。例如,为了更好地理解这一点,假设我们的初始目录结构如下:

    初始目录结构。

    .
    └── [NAME]
        ├── [NAME].controller.js
        ├── [NAME].html
        └── TEST.html
    

    ...如果我们运行脚本搜索字符串[NAME] 以替换为字符串TEST - 那么我们就有一个潜在的问题。如果我们将[NAME].html 重命名为TEST.html,我们将覆盖现有的TEST.html。我们生成的目录结构如下:

    可能产生的目录结构。

    .
    └── TEST
        ├── TEST.controller.js
        └── TEST.html
    

    仅在尚未采用新的结果路径时才重命名资产,我们可以避免这种丢失数据的潜在有害情况。

    实际生成的目录结构。

    .
    └── TEST
        ├── TEST.controller.js
        ├── [NAME].html
        └── TEST.html
    

    当不应重命名资产时(因为它会导致数据丢失),脚本当前会报告这些实例。给定初始目录结构(上图),您的控制台将记录以下内容:

    1 path(s) not renamed. Name is already taken:

    + FolderName/TEST/[NAME].js --> FolderName/TEST/TEST.js

    以下要点使用上述方法。该解决方案是在 ES5 中编写的,因此它适用于旧版本的 nodejs,但是可以简单地对其进行修改以使用 ES6 语法。

    示例节点脚本 1

    var shell = require('shelljs');
    
    var ROOT_DIR = './FolderName/'; // <-- Directory to search in relative to cwd.
    var FIND_STR = '[NAME]';        // <-- String to find
    var REPLACE_STR = 'TEST';       // <-- Replacement string
    
    var issues = [];
    
    // 1. Obtain all paths in the root directory.
    shell.find(ROOT_DIR)
    
      // 2. Exclude:
      //    - hidden files/folders (i.e. assets names starting with a dot)
      //    - Assets (i.e. at the end of the path) that do not contain `FIND_STR`
      .filter(function(_path) {
        var isVisible = _path.split('/').pop().indexOf('.', 0) !== 0,
          assetHasFindStr = _path.split('/').pop().indexOf(FIND_STR) > -1;
        return (assetHasFindStr && isVisible);
      })
    
      // 3. Sort paths by its depth in descending order.
      .sort(function(a, b) {
        return (b.split('/') || []).length - (a.split('/') || []).length;
      })
    
      // 4. Replace last instance of string to find with replace string and rename.
      .forEach(function (_path) {
        var firstPart = _path.substring(0, _path.lastIndexOf(FIND_STR)),
          lastPart = _path.substring(_path.lastIndexOf(FIND_STR, _path.length)),
          newPath = firstPart + lastPart.replace(FIND_STR, REPLACE_STR);
    
        // Only rename if `newPath` is not already taken otherwise log them.
        if (!shell.test('-e', newPath)) {
          shell.mv(_path, newPath);
        } else {
          issues.push(_path + ' --> ' + newPath);
        }
      });
    
    // 5. Log any paths that were not renamed because its name is already taken.
    if (issues.length) {
      shell.echo(issues.length + ' path(s) not renamed. Name is already taken:');
      issues.forEach(function(issue) {
        shell.echo('+ ' + issue);
      });
    }
    

    解决方案 B

    您的要求也可以通过安装和使用renamer来实现。

    $ npm i -D renamer

    然后使用shelljs 调用renamer 命令。


    示例节点脚本 2

    const shell = require("shelljs");
    shell.exec('node_modules/.bin/renamer --find \"[NAME]\" --replace \"TEST\" \"FolderName/**\"', { silent:true });
    

    示例节点脚本 3

    如果你需要一些更简洁的东西,(虽然它会产生额外的依赖),你可以使用shelljs-nodecli

    $ npm i -D shelljs-nodecli

    然后调用renamer命令如下图:

    const nodeCLI = require("shelljs-nodecli");
    nodeCLI.exec('renamer', '--find \"[NAME]\" --replace \"TEST\" \"FolderName/**\"', { silent:true });
    

    注意,使用shelljs-nodecli 可以避免手动查看node_modules 目录来查找二进制renamer 文件。即

    shell.exec('node_modules/.bin/renamer ...
    

    变成……

    nodeCLI.exec('renamer' ...
    

    【讨论】:

      猜你喜欢
      • 2013-02-13
      • 2017-12-09
      • 1970-01-01
      • 2019-04-28
      • 2014-08-11
      • 2017-10-23
      • 1970-01-01
      • 2016-08-22
      • 2020-08-13
      相关资源
      最近更新 更多