我已经关注了这个网站上关于这个主题的很多帖子,但我仍然明显做错了什么......
我的目标是在两个不同的数组中定义值,然后使用 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
用于移出位置参数列表的最前面的元素。