在 package.json 中使用 shx 删除和复制文件

delete and copy files using shx in package.json

我有两个 npm 项目,一个是名为 server-projectGatsby project called web-project and another one is Express 项目。

这是项目的文件结构:

- projects
   - web-project
      - public
      - package.json
   - server-project
      - package.json
      - public

如果我 运行 在 web-project/package.json 中命令 npm run build,我想为 Web 项目生成构建文件,删除文件夹 server-project/public 中的内容并从中复制内容web-project/publicserver-project/public.

我尝试使用 shx,它在 web-project 本地安装,但它不起作用。 Gatsby 生成构建文件,但 shx 不复制它们。

web-project/package.json

"scripts": {
    "build": "gatsby build && (shx find ../server-project/public -type f -delete && shx cp -R public/* ../server-project/public)"
    ...
  },

npm 调试日志文件:

0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli   '/usr/local/Cellar/node/12.1.0/bin/node',
1 verbose cli   '/usr/local/bin/npm',
1 verbose cli   'run',
1 verbose cli   'build'
1 verbose cli ]
2 info using npm@6.9.0
3 info using node@v12.1.0
4 verbose run-script [ 'prebuild', 'build', 'postbuild' ]
5 info lifecycle tau@1.0.0~prebuild: tau@1.0.0
6 info lifecycle tau@1.0.0~build: tau@1.0.0
7 verbose lifecycle tau@1.0.0~build: unsafe-perm in lifecycle true
8 verbose lifecycle tau@1.0.0~build: PATH: /usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/and/devel/tau/web-project/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
9 verbose lifecycle tau@1.0.0~build: CWD: /Users/and/devel/tau/web-project
10 silly lifecycle tau@1.0.0~build: Args: [
10 silly lifecycle   '-c',
10 silly lifecycle   'gatsby build && (shx find ../server-project/public -type ' +
10 silly lifecycle     'f -delete && shx cp -R public/* ../server-project/public)'
10 silly lifecycle ]
11 silly lifecycle tau@1.0.0~build: Returned: code: 1  signal: null
12 info lifecycle tau@1.0.0~build: Failed to exec build script
13 verbose stack Error: tau@1.0.0 build: `gatsby build && (shx find ../server-project/public -type f -delete && shx cp -R public/* ../server-project/public)`
13 verbose stack Exit status 1
13 verbose stack     at EventEmitter.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:301:16)
13 verbose stack     at EventEmitter.emit (events.js:196:13)
13 verbose stack     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack     at ChildProcess.emit (events.js:196:13)
13 verbose stack     at maybeClose (internal/child_process.js:1000:16)
13 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:267:5)
14 verbose pkgid tau@1.0.0
15 verbose cwd /Users/and/devel/tau/web-project
16 verbose Darwin 18.7.0
17 verbose argv "/usr/local/Cellar/node/12.1.0/bin/node" "/usr/local/bin/npm" "run" "build"
18 verbose node v12.1.0
19 verbose npm  v6.9.0
20 error code ELIFECYCLE
21 error errno 1
22 error tau@1.0.0 build: `gatsby build && (shx find ../server-project/public -type f -delete && shx cp -R public/* ../server-project/public)`
22 error Exit status 1
23 error Failed at the tau@1.0.0 build script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

我尝试将构建命令更改为仅将文件从一个文件夹复制到另一个文件夹:

web-project/package.json

{
  "name": "tau",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "develop": "gatsby develop",
    "build": "shx cp -R public/* ../server-project/public",
    "serve": "gatsby serve",
    "start": "node server.js",
    "gh-pages": "gatsby build --prefix-paths && gh-pages -d public",
    "lint": "eslint src --fix",
    "dev": "(shx --silent rm -rf public .cache || shx true) && gatsby develop",
    "server": "cross-env NODE_ENV=development DEBUG=api nodemon server.js"
  },
  "author": "PI",
  "license": "MIT",
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^1.2.25",
    "@fortawesome/free-brands-svg-icons": "^5.12.0",
    "@fortawesome/free-solid-svg-icons": "^5.11.2",
    "@fortawesome/react-fontawesome": "^0.1.7",
    "@kunukn/react-collapse": "1",
    "@material-ui/core": "^4.9.5",
    "@popperjs/core": "^2.3.3",
    "add": "^2.0.6",
    "axios": "^0.19.0",
    "basic-auth": "^2.0.1",
    "bootstrap": "4.2.1",
    "chart.js": "^2.9.3",
    "d3-node": "^2.2.1",
    "debug": "^4.1.1",
    "font-awesome": "4.7.0",
    "framer-motion": "^1.10.3",
    "gatsby": "^2.18.12",
    "gatsby-plugin-canonical-urls": "^2.3.0",
    "gatsby-plugin-dark-mode": "^1.1.0",
    "gatsby-source-prismic-graphql": "^3.5.0",
    "gatsby-transformer-remark": "^2.6.53",
    "lodash.get": "^4.4.2",
    "lodash.groupby": "^4.6.0",
    "lodash.pickby": "^4.6.0",
    "marked": "^0.8.0",
    "moment": "^2.24.0",
    "prismic-reactjs": "^1.3.1",
    "prop-types": "^15.7.2",
    "react-chartjs-2": "^2.8.0",
    "react-collapsible": "^2.6.3",
    "react-d3-components": "^0.9.1",
    "react-d3-library": "^1.1.8",
    "react-headroom": "^3.0.0",
    "react-helmet": "^5.2.0",
    "react-lazyload": "^2.6.5",
    "react-moment": "^0.9.6",
    "react-onclickout": "^2.0.8",
    "react-popper": "^2.2.3",
    "react-popper-tooltip": "^2.11.1",
    "react-responsive": "^8.0.1",
    "react-scroll-to": "^3.0.0-beta.3",
    "react-sidebar": "^3.0.2",
    "react-slick": "^0.25.2",
    "react-svg-donuts": "^1.0.0",
    "react-telegram-embed": "^0.0.10",
    "react-toastify": "^5.4.1",
    "react-twitter-embed": "^3.0.3",
    "react-window": "^1.8.5",
    "reactstrap": "^8.4.1",
    "slick-carousel": "^1.8.1",
    "underscore": "^1.9.1",
    "yarn": "^1.21.1"
  },
  "devDependencies": {
    "@prototype-interactive/eslint-config": "^0.1.1",
    "autoprefixer": "^9.4.4",
    "dotenv": "^8.2.0",
    "eslint": "^5.12.0",
    "gatsby-plugin-google-analytics": "^2.3.0",
    "gatsby-plugin-postcss": "^2.0.2",
    "gatsby-plugin-postcss-sass": "^1.0.22",
    "gatsby-plugin-react-helmet": "^3.0.5",
    "gatsby-plugin-sass": "^2.0.7",
    "gatsby-source-filesystem": "^2.2.2",
    "gatsby-transformer-json": "^2.1.7",
    "gh-pages": "^2.0.1",
    "husky": "^1.3.1",
    "prettier": "^1.15.3",
    "pretty-quick": "^1.8.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "shx": "^0.3.2",
    "svg-sprite-loader": "^4.1.3"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/PI/gatsby-react-boilerplate.git"
  },
  "bugs": {
    "url": "https://github.com/PI/gatsby-react-boilerplate/issues"
  },
  "homepage": "https://github.com/PI/gatsby-react-boilerplate#readme"
}

npm 调试日志文件:

0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli   '/usr/local/Cellar/node/12.1.0/bin/node',
1 verbose cli   '/usr/local/bin/npm',
1 verbose cli   'run',
1 verbose cli   'build'
1 verbose cli ]
2 info using npm@6.9.0
3 info using node@v12.1.0
4 verbose run-script [ 'prebuild', 'build', 'postbuild' ]
5 info lifecycle tau@1.0.0~prebuild: tau@1.0.0
6 info lifecycle tau@1.0.0~build: tau@1.0.0
7 verbose lifecycle tau@1.0.0~build: unsafe-perm in lifecycle true
8 verbose lifecycle tau@1.0.0~build: PATH: /usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/and/devel/tau/web-project/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
9 verbose lifecycle tau@1.0.0~build: CWD: /Users/and/devel/tau/web-project
10 silly lifecycle tau@1.0.0~build: Args: [ '-c', 'shx cp -R public/* ../web-project-project/public' ]
11 silly lifecycle tau@1.0.0~build: Returned: code: 1  signal: null
12 info lifecycle tau@1.0.0~build: Failed to exec build script
13 verbose stack Error: tau@1.0.0 build: `shx cp -R public/* ../web-project-project/public`
13 verbose stack Exit status 1
13 verbose stack     at EventEmitter.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:301:16)
13 verbose stack     at EventEmitter.emit (events.js:196:13)
13 verbose stack     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack     at ChildProcess.emit (events.js:196:13)
13 verbose stack     at maybeClose (internal/child_process.js:1000:16)
13 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:267:5)
14 verbose pkgid tau@1.0.0
15 verbose cwd /Users/and/devel/tau/web-project
16 verbose Darwin 18.7.0
17 verbose argv "/usr/local/Cellar/node/12.1.0/bin/node" "/usr/local/bin/npm" "run" "build"
18 verbose node v12.1.0
19 verbose npm  v6.9.0
20 error code ELIFECYCLE
21 error errno 1
22 error tau@1.0.0 build: `shx cp -R public/* ../web-project-project/public`
22 error Exit status 1
23 error Failed at the tau@1.0.0 build script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

您可以从 shx cp -R public/* ../server-project/public 中删除通配符,因为递归标志将选取源目录中的所有内容。这应该让你得到你想要的 -

shx cp -R public ../server-project/public

您似乎已经在构建系统中使用了 find。我利用 tar 并在我维护的一些构建系统中找到了大量内容,这对我来说效果很好。 tar 可以很好地处理各种边缘情况,因为它最初是作为备份实用程序编写的。

在tar中,-是表示stdin或stdout的文件名,-T指定要备份的文件列表,-C指定其工作目录。这使您可以将一堆东西与管道粘合在一起,并完成一些非常酷的事情。例如,

(cd /path/to/src/files && find . -type f -name) \
| tar -T - -cf - \
| tar -C /path/to/dest -xvf -
  • 子 shell 让我在不影响其他命令的情况下查找目录
  • find 在 stdout 上生成文件列表
  • 第一个 tar 读取要归档到标准输入的文件列表
  • 第一个 tar 在 stdout
  • 上生成存档
  • 第二个 tar 将目录更改为 /path/to/dest
  • 然后它在 stdin
  • 上提取存档
  • 和 'v' 打印提取的文件列表

这将为您保留所有权、组成员资格、权限、符号链接、目录结构,整个九码。即使是带空格的文件名也不会搞砸。 :)

这部分存在的主要问题:

shx find ../server-project/public -type f -delete

-type f 标志在 shx 中不受支持(参见 issue #177)。据推测,-delete 标志也不被支持。

考虑用执行以下操作的 node.js 脚本替换该部分:

  1. 利用shelljs find方法。
  2. 使用 forEach().
  3. 迭代找到的路径名数组
  4. forEach 回调的正文中检查它是否是一个文件并相应地删除。

例如,将其全部放入名为 build:

的单个 npm 脚本中
"scripts": {
  ...
  "build": "gatsby build && node -e \"var sh = require('shelljs'), fs = require('fs'); sh.find('../server-project/public').forEach(function (file) { if (!fs.statSync(file).isDirectory()) { sh.rm(file); }});\" && shx cp -R \"public\" \"../server-project/public\""
  ...
}

备注:

  1. 您在 OP 中尝试使用 find ../server-project/public -type f -delete 意味着您的意图是(仅)从 ../server-project/public 中删除所有文件(n 级深)- 但不要删除 ../server-project/public 中的任何文件夹(再次 n 级深)。这就是上述示例中的 node -e \"...\" 命令复制的内容。

  2. nodejs命令行选项-e用于评估内联Javascript,本质上是这样的:

    var sh = require('shelljs'),
        fs = require('fs');
    
    sh.find('../server-project/public').forEach(function (file) {
        if (!fs.statSync(file).isDirectory()) {
            sh.rm(file);
        }
    });
    
  3. 最后一个 shx cp -R ... 命令中的 globstar (*) 和包含复合命令的括号 (...) 已被省略。