sed 命令实践: 升级 sequelize.js 时批量替换字符串

sequelize 是 Node 中使用比较多的一个 ORM 库,最近计划将项目中的 sequelize 升级至 V5 版本。javascript

根据 升级文档,其中一项是即将禁用 String based operators,使用 Sequelize.Op 等 Symbol operators 来代替。html

operator 主要用在查询条件中,用以生成查询条件,如java

const where = {
  age: {
    $lte: 10 
  }
}

// 替换为
const replaceWhere = {
  age: {
    [Op.lte]: 10
  }
}
复制代码
const operatorsAliases = {
  $eq: Op.eq,
  $ne: Op.ne,
  $gte: Op.gte,
  $all: Op.all,
  $in: Op.in,
  ...moreAliase
}
复制代码

简而言之,须要把本项目中的全部查询条件, 从 operatorsAliases 左边的替换为右边的。这也是本篇文章的主要内容git

本文连接: shanyue.tech/post/sequel…github

准备工做

在开始工做以前,须要先把 git 的工做区和暂存区清理干净,避免替换过程当中形成没法回退的尴尬局面。正则表达式

把工做区和暂存区清理干净的意思就是,先把能 commit 的 commit 掉,不能 commit 的 stash 掉,固然切个新分支就更好了。shell

VS Code 全局替换

在刚开始随手手动替换了几个以后,以为这样也不是办法,决定开始使用 VS Code 的全局替换。vim

首先思考一个查询的 operator 会出现的位置,无外乎如下几种浏览器

where.age = { $lte: 10 }
where.age.$lte = 10
where.age['$lte'] = 10
复制代码

另外,顺序很重要,从最具体到抽象的顺序以下bash

['$lte', Op.lte],
['.$lte', [Op.lte]],
['$lte', [Op.lte]]
复制代码

而后,按照顺序挨个替换就行了,但替换了几个知乎,我发现...个人耐心实在有限

> Object.keys(operatorsAliases).length
34
复制代码

我须要替换 34 * 3 = 102 次,这也不能怪我烦啊,搁谁谁都没有耐心

使用 sed 命令替换文件

多掌握一个命令是多么重要

先来一个 hello, world 版的 sed 命令,如下命令把 hello 替换成 word

恩,sed 替换的语法和 vim 简直如出一辙,这告诉咱们掌握 vim 多么重要...

$ echo hello | sed "s/hello/world/g"
world
复制代码

根据上一部分所讲的规则,写一个 sed 文件 (replace.sed),对示例(test.js)作一个测试

# replace.sed
s/'$lte'/Op.lte/g
s/.$lte/[Op.lte]/g
s/$lte/[Op.lte]/g
复制代码
where.age = { $lte: 10 }
where.age.$lte = 10
where.age['$lte'] = 10
复制代码

作了简单的测试,输入如下命令,看起来工做地还不错

$ sed -f replace.sed test.js
where.age = {[Op.lte]: 10 }
where.age[Op.lte] = 10
where.age[Op.lte] = 10
复制代码

可是有 34 个 alias 须要替换,利用浏览器的控制台生成 sed 文件

> Object.keys(operatorsAliases).map(op => op.slice(1)).flatMap(op =>  [`s/'\$${op}\b'/Op.${op}/g`, `s/\.\$${op}\b/[Op.${op}]/g`, `s/\$${op}\b/[Op.${op}]/g`]).join('\n')
s/'$eq'/Op.eq/g
s/.$eq/[Op.eq]/g
s/$eq/[Op.eq]/g
s/'$ne'/Op.ne/g
s/.$ne/[Op.ne]/g
s/$ne/[Op.ne]/g
...
...
复制代码

虽然生成的命令有些简单粗暴...,不过简单粗暴的东西就是好用

替换项目下全部文件

只剩下一个问题,如何列出当前路径下的全部文件

多掌握一个命令是多么重要

我把全部我能想到的命令给列下来

  • find . 应该能够排除掉 .gitignores 所列文件,但好像有点麻烦,我历来没用过。
  • ls -R 格式不够友好
  • tree 可读性不错,但机器可读性太差了

如何排除文件夹能够参考 How to exclude a directory in find . command

以上三个命令都不太好用。柳暗花明又一村,这里有一个更简单而又恰到好处的命令

git ls-files
复制代码

关于 git 的更多命令,能够参考 Git Cheat Sheets

此时,shell 命令以下,-i 表明直接替换文件,-i "" 表明替换时文件名不添加后缀,为啥必定要写个空字符串,由于 MAC 下的 sed 命令就是如此丧心病狂。

$ sed -i "" -f replace.sed $(git ls-files)
复制代码

不过,这时候有新的问题产生了,在 git diff 时发现有一些模板中带有 $index ,也会被替换成 [Op.in]dex,这是不指望的结果

git checkout .
复制代码

使用正则匹配

使用 \b 匹配单词,完美解决问题。

s/'\$eq\b'/Op.eq/g
s/\.\$eq\b/[Op.eq]/g
s/\$eq\b/[Op.eq]/g
复制代码

不过,在 MAC 下并不支持 \b,能够拿如下命令作个试验。这时候在 MAC 下须要安装 gnu-sed,终于把 MAC 下的 sed 命令替换掉了

$ echo "hello" | sed "s/\bhello\b/world/g"
hello
$ brew install gnu-sed
$ echo "hello" | gsed "s/\bhello\b/world/g"
world
 # 必定必定要用双引号括起来
$ echo "hello" | gsed s/\bhello\b/world/g
hello
复制代码

这里有一个很重要的点,即sed命令必定要用双引号给括起来

使用 js 生成新的 sed 命令

Object.keys(operatorsAliases).map(op => op.slice(1)).flatMap(op =>  [`s/'\\$${op}\\b'/Op.${op}/g`, `s/\\.\\$${op}\\b/[Op.${op}]/g`, `s/\\$${op}\\b/[Op.${op}]/g`]).join('\n')
复制代码

最后执行命令,成功替换所有字符

# -i 表明直接替换文件,-r 表明支持扩展的正则表达式
$ gsed -i -r -f r.sed $(git ls-files| grep -v src/data)
 $ git diff --shortstat
63 files changed, 293 insertions(+), 293 deletions(-)
复制代码

欢迎关注个人公众号山月行,在这里记录着个人技术成长,欢迎交流

欢迎关注公众号山月行,在这里记录个人技术成长,欢迎交流
相关文章
相关标签/搜索