將 sed 與 2 個數組中定義的變數一起使用

將 sed 與 2 個數組中定義的變數一起使用

我已經關注了這個網站上關於這個主題的很多帖子,但我仍然明顯做錯了什麼...

我的目標是在兩個不同的陣列中定義值,然後使用 sed 用第二個陣列的文字搜尋第一個陣列中定義的文字字串。

程式碼如下:


#!/bin/bash

# define variables
defaultdirs=( Templates Documents Music Pictures Videos )
customdirs=( custom\/templates custom\/documents custom\/music custom\/pictures custom\/videos )

# replace text strings
for index in ${!defaultdirs[*]}
    do
    echo ${defaultdirs[$index]} will become ${customdirs[$index]}
    sed -i 's/${defaultdirs[$index]}/${customdirs[$index]}/g' ~/Desktop/scripts/test_replace.txt
done

echo 輸出正確的字串,但 sed 沒有獲得正確的訊息,因為文字檔案保持不變。

想法?

作為參考,這是 test_replace.txt 的內容

# This file is written by xdg-user-dirs-update
# If you want to change or add directories, just edit the line you're
# interested in. All local changes will be retained on the next run.
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
# absolute path. No other format is supported.
# 
XDG_DESKTOP_DIR="$HOME/Desktop"
XDG_DOWNLOAD_DIR="$HOME/Downloads"
XDG_TEMPLATES_DIR="$HOME/Templates"
XDG_PUBLICSHARE_DIR="$HOME/Public"
XDG_DOCUMENTS_DIR="$HOME/Documents"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_VIDEOS_DIR="$HOME/Videos"

答案1

第一個問題:單引號字串中沒有展開的變數。

第二個問題:照原樣,s///g修復第一個問題後,替換中的斜線將破壞指令。使用不同的分隔符號s

第三個(較小的)問題:您在同一個文件上運行sed多次,這不是很有效,並且-i就地編輯的選項是非標準的,並且不同的實現確實提供了不同的行為(常見情況人們遇到的問題是GNU 版本不需要參數,但Mac OS 版本需要)。當想要編輯文件並儲存變更時,通常最好使用專用文件編輯器,例如edex

只需一次呼叫即可完成所有操作ed

#!/bin/bash

# define variables
defaultdirs=(Templates Documents Music Pictures Videos)
customdirs=(custom/templates custom/documents custom/music custom/pictures custom/videos)

# replace text strings
(for index in ${!defaultdirs[*]}; do
     echo "${defaultdirs[$index]} will become ${customdirs[$index]}" >&2
     echo "g/${defaultdirs[$index]}/s|${defaultdirs[$index]}|${customdirs[$index]}|g"
 done;
 echo w) | ed -s test_replace.txt

X will become Y將訊息發送到標準輸出而不是標準錯誤的替代方案,並ed協程,將各個命令重定向到其輸入,而不是使用管道:

#!/bin/bash

# define variables
defaultdirs=(Templates Documents Music Pictures Videos)
customdirs=(custom/templates custom/documents custom/music custom/pictures custom/videos)

coproc ED { ed -s test_replace.txt; } 2>/dev/null

# replace text strings
for index in ${!defaultdirs[*]}; do
     echo "${defaultdirs[$index]} will become ${customdirs[$index]}"
     echo "g/${defaultdirs[$index]}/s|${defaultdirs[$index]}|${customdirs[$index]}|g" >&${ED[1]}
done
printf '%s\n' w q >&${ED[1]}
wait $ED_PID

答案2

您也可以將所有s替代命令收集到一個輸入中:sed

for index in ${!defaultdirs[*]}
  do   echo "s#${defaultdirs[$index]}#${customdirs[$index]}#g"
  done | sed -f- ~/Desktop/scripts/test_replace.txt

答案3

該問題,如肖恩已經指出,是因為您建立的sed腳本有語法錯誤。語法錯誤來自於您嘗試在使用相同字元作為分隔符號的命令/中使用這一事實。s

嘗試透過轉義/數組中字串中的來抵消此問題customdirs,但您實際上需要在字串中\\/插入轉義。\

相反,這是一種不同的方法:

find_strings=( Templates Documents Music Pictures Videos )
replace_strings=( custom/templates custom/documents custom/music custom/pictures custom/videos )

set -- "${find_strings[@]}"

sed_stmts=( )
for replace_string in "${replace_strings[@]}"; do
   # sed_stmts+=( -e 's,\("$HOME/\)'"$1"'",\1'"$replace_string"'",' )

    # simpler, but less precise:
    #    sed_stmts+=( -e "s,$1,$replace_string," )
    # alternatively:
    #    sed_stmts+=( -e "s/${1//\//\\/}/${replace_string//\//\\/}/" )

    shift
done

sed "${sed_stmts[@]}" test_replace.txt >new-test_replace.txt

我還冒昧地通過匹配"$HOME/前綴字符串和最終的".

這最終會sed像這樣呼叫:

sed -e 's,\("$HOME/\)Templates",\1custom/templates",' -e 's,\("$HOME/\)Documents",\1custom/documents",' -e 's,\("$HOME/\)Music",\1custom/music",' -e 's,\("$HOME/\)Pictures",\1custom/pictures",' -e 's,\("$HOME/\)Videos",\1custom/videos",' test_replace.txt

sed它透過在數組中建立一組語句來實現這一點sed_stmts,然後在對sed.

兩個數組中兩組字串的配對是透過使用 將其中一個數組分配給位置參數列表set,然後迭代另一個數組來完成的。在每次迭代中,shift用於移出位置參數清單的最前面的元素。

相關內容