在文件的每一行中查找一個字元串,然後使用 bash 替換同一行中的另一個字元串
這是我的腳本:
#! /bin/bash # C-band Edit dqt='"' str5="polarization=${dqt}2${dqt}" for x in {3600..4200} do; sed -i "/$str5.*$x/s/$x/$((x-600))/" satellites.xml done
我想做的就是在satellites.xml中有str5的所有行上用3600到4200之間的x-600替換數字x,但上面的腳本給了我語法錯誤
我突然想到你不是指文字字元串
x-600
,你的意思是“從 3600 到 4200 之間的每個值中減去 600”。在這種情況下,使用 perl,而不是 sed:echo "$str5. foo bar 3200 3964 4155 4200 4255" | perl -p -e "if (/$str5/) {s/3[6789]\d\d|4[01]\d\d|4200/$&-600/eg}" polarization="2". foo bar 3200 3364 3555 3600 4255
這使用 perl 的
/e
正則表達式修飾符來導致右側 ($&-600
) 被評估為 perl 表達式。$&
是一個包含匹配值的 perl 變數,因此$&-600
意味著從該值中減去 600。如果那是您真正想要的,我會在下面留下我的原始答案。也因為它有有用的解釋,這些解釋仍然與上面的 perl 答案有些相關。
與 sed 一樣,perl 有一個
-i
就地編輯選項,因此您可以將其直接應用到您的 satellites.xml 文件中。詳情請參閱man perlrun
。perl -i -p -e "if (/$str5/) {s/3[6789]\d\d|4[01]\d\d|4200/$&-600/eg}" satellites.xml
另外值得注意的是:在處理 XML 文件時,您可能應該使用 XML 解析器。幸運的是,perl 有幾個可供選擇,例如XML::Parser或集合libxml-perl。
我沒有您實際輸入的範例,因此我製作了一個範例來展示什麼會改變,什麼不會:
str5='polarization="2"' echo "$str5. foo bar 3500 3964 4155 4200 4255" | sed -e "/$str5/ {s/3[6-9][0-9][0-9]/3-600/g; s/4[01][0-9][0-9]/4-600/g; s/4200/4-600/g}" polarization="2". foo bar 3500 3-600 4-600 4-600 4255
大致翻譯成英文,就是“如果目前行包含$str5(
polarization="2"
),則對其應用這三個s///
操作”。筆記:
無需從 3600..4200 循環。這將在一次執行中進行所有更改
sed
,而不是 600 次執行。您希望更改 3600-4200 的值。這意味著您需要 3 次搜尋和替換操作:
- 一個用於 3600-3999
- 一個用於 4000-4199
- 一個正好 4200
或者,這可以通過兩個
s///
操作來完成,將最後兩個合併為一個:sed -e "/$str5/ { s/3[6-9][0-9][0-9]/3-600/g; s/4[01][0-9][0-9]\|4200/4-600/g }"
- 可能還有許多其他方法可以優化正則表達式,但是您這樣做的次數越多,將來閱讀和修改它們的難度就越大。
3964
,4155
,4200
並被更改。3500
而4255
不是,它們超出了所需的範圍。[0-9]
在某些語言環境(該範圍內還有其他字元)中不會像您期望的那樣工作。我不確切知道哪些語言環境,但我已經看到它經常被提及,足以知道[0-9]
不能完全依賴它。如果這影響到您,您可以使用[[:digit:]]
代替[0-9]
,或使用perl -p
代替sed
(因此您可以使用\d
代替[0-9]
)。同樣適用於 range[6-9]
,請[6789]
改用。- 最後,要非常小心你放入
$str5
變數中的內容。因為它被插入到sed
命令中,所以很容易破壞 sed 腳本(例如,如果$str5
包含 a/
,它將破壞/$str5/
匹配)。sed 不知道它的腳本部分來自一個 shell 變數,它所看到的只是它被賦予執行的腳本。此外,整個字元串將被sed 解釋為正則表達式- 這意味著正則表達式元字元不會被解釋為文字字元,除非它們使用
\
. eg.
將被解釋為“任何字元”而不是文字點,除非它被轉義為\.