使用帶有變數的 sed 命令苦苦掙扎
所以我在 bash 腳本中定義了以下變數:
new_commit="back:h3912kk" old_commit="back:1.0.1" file = docker-compose.yml
然後我有這個 yml
$file
:version: '3' services: mongo: container_name: mongo image: mongo:3.4 volumes: - /home/mongo_files:/data/db networks: my_network: back: container_name: back image: index.docker.io/gg/back:1.0.1 networks: my_network networks: my_network:
我想換行
image: index.docker.io/gg/back:1.0.1
為了
image: index.docker.io/gg/back:h3912kk
我
sed
在我的 bash 腳本中使用以下命令:echo sed -i "s/$old_commit/$new_commit/" $file
但是會出現以下錯誤:
sed: -e expression #1, char 12: unterminated `s' command
當然 docker-compose 文件不會更新。
我究竟做錯了什麼?
如果我執行 echo 命令,則會顯示:
sed -i s/back:1.0.1 /back:h3912kk/ docker-compose.yml
為什麼 echo 不在同一行顯示命令?
編輯:
正如@Kusalananda♦ 所說,old_commit 變數不是靜態文件,這就是我猜為什麼我無法重現您提供的
$old_commit
相關答案的主要原因(我雖然不會假設有問題)為了獲得
old_commit
參數,我使用以下內容在 yml 文件中查找滿足我感興趣的條件的行,這是我的完整程式碼:output='back:h3912kk' # here I split the name of the repo and the commit by ':' readarray -d ':' -t array_new_commit <<< "$output" repo="${array_new_commit[0]}" file=docker-compose.yml # here I loop over the lines of the yml file: while read -r line; do # if a line contains the repo name str and image str I generate a line with the new commit if [[ $line == *$repo* && $line == *"image"* ]]; then # old_line=$line readarray -d '/' -t array_old_commit <<< "$line" old_commit=${array_old_commit[-1]} # new_line="${array_old_commit[0]}:${array_old_commit[1]}:$new_commit" fi done<"$file"
不知何故(不知道為什麼,我是 bash 的新手)以這種方式生成這個變數實際上不允許我像使用靜態變數一樣使用它。(我真的很想明白這一點)
您能否給我一個解決方法,將
old_commit
變數替換new_commit
為 yml 文件?再次感謝 Kusalananda♦ 指出這個問題
PD 我使用的是 ubuntu 0.18.4 發行版
編輯: 使用第二段程式碼:
yq -Y --arg new "$new_commit" --arg reposit "$repo" \ '".services." + $reposit + ".image |=index.docker.io/gg/" + $new' docker-compose.yml
我的輸出是:
.services.back.image |=index.docker.io/gg/back:h3912kk ...
您不應使用
sed
YAML 文件或以任何結構化文件格式(YAML、JSON、XML 等)編寫的文件。相反,應使用為該文件格式編寫的解析器。對於 YAML,有兩個為 shell 編寫的好工具,都被混淆地稱為
yq
. 來自https://kislyuk.github.io/yq/是一個易於使用的yq
包裝器(並且是一個 JSON 解析器)。jq``jq
對於這個答案的一部分,我將假設您已經定義了兩個 shell 變數
old_commit
,並且new_commit
與您在問題中的程式碼中顯示的完全一樣。您稍後顯示的輸出告訴我,您實際上並沒有將它們分配為靜態字元串,而是通過其他一些您沒有提及的過程(其中$old_commit
似乎有一個尾隨換行符),所以我不能說太多關於它,並會忽略它。要使用此實現更改 under 、 under
image
鍵的值,您可以使用back``services``yq
yq -Y '.services.back.image |= "index.docker.io/gg/back:h3912kk"' file.yml
或者,使用您的
$new_commit
值:new_commit='back:h3912kk' yq -Y --arg new "$new_commit" \ '.services.back.image |= "index.docker.io/gg/" + $new' file.yml
請注意,我們使用創建變數將 shell 變數的值導入到表達式中*,*而不是將其直接注入到表達式字元串中。這樣我們可以確保值被正確編碼,並且還可以確保我們的程式碼中沒有任何程式碼注入漏洞。
--arg``yq``$new``yq
或者,不必提及
index.docker.io/gg/
字元串:new_commit='back:h3912kk' yq -Y --arg new "$new_commit" \ '.services.back.image |= sub("[^/]*$"; "") + $new' file.yml
或者,為儲存庫使用單獨的變數:
new_commit='back:h3912kk' yq -Y --arg new "$new_commit" --arg repo 'back' \ '.services[$repo].image |= sub("[^/]*$"; "") + $new' file.yml
我們還可以使用
$old_commit
字元串來查找要替換的正確圖像,而不是通過靜態路徑:old_commit='back:1.0.1' new_commit='back:h3912kk' yq -Y --arg old "$old_commit" --arg new "$new_commit" \ '(.services[].image | select(endswith($old))) |= (rtrimstr($old) + $new)' file.yml
在這裡,我們選擇所有
.services.*.image
以.$old_commit``$new_commit
給定問題中的範例 YAML 文件,上述每個命令都會生成
version: '3' services: mongo: container_name: mongo image: mongo:3.4 volumes: - /home/mongo_files:/data/db networks: my_network: null back: container_name: back image: index.docker.io/gg/back:h3912kk networks: my_network networks: my_network: null
並且您可以輕鬆地將輸出重定向到新文件(或使用 就地編輯 YAML 文件
--in-place
)。
yq
您從例如 Ubuntu 上獲得的工具snap
是不同的,我傾向於使用它將 YAML 轉換為 JSON(然後再轉換回來):yq -j e file.yml | jq '.services.back.image |= "index.docker.io/gg/back:h3912kk"' | yq -P e
給定範例 YAML 文件,這將產生與上述相同的輸出。上面的
jq
表達式可以與此答案頂部的任何表達式交換,具有相同的語義。