1

我有这个文件结构:

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
4

1 回答 1

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. 仅用每个路径中[NAME]的替换字符串(例如)替换要查找的字符串的最后一个实例(例如)。TEST然后最后使用shelljs mv命令应用新路径。

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

初始目录结构。

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

...如果我们运行脚本搜索[NAME]要替换为字符串的字符串TEST- 那么我们就有一个潜在的问题。如果我们要重命名[NAME].html为 beTEST.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文件。IE

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

变成……

nodeCLI.exec('renamer' ...
于 2017-11-03T15:55:55.403 回答