我已經關注了這個網站上關於這個主題的很多帖子,但我仍然明顯做錯了什麼...
我的目標是在兩個不同的陣列中定義值,然後使用 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 版本需要)。當想要編輯文件並儲存變更時,通常最好使用專用文件編輯器,例如ed
或ex
。
只需一次呼叫即可完成所有操作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
用於移出位置參數清單的最前面的元素。