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(-)
複製代碼

歡迎關注個人公衆號山月行,在這裏記錄着個人技術成長,歡迎交流

歡迎關注公衆號山月行,在這裏記錄個人技術成長,歡迎交流
相關文章
相關標籤/搜索