将 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用于移出位置参数列表的最前面的元素。

相关内容