【问题标题】:Search and replace shell variable with multiline shell variable用多行 shell 变量搜索和替换 shell 变量
【发布时间】:2015-03-10 20:34:02
【问题描述】:

我知道有很多类似的问题,但我猜是因为搜索/替换变量的格式,它们不适用于我的情况。

我正在编写的整个脚本将读取一个文本文件(使用 grep 和 awk 的组合)来创建两个 shell 变量,$find 和 $replace。 $find 是包含各种字符的单行字符串。 $replace 是一个包含各种字符的多行字符串。

示例:

echo "$find"

返回

type result input1 another_input random_input<10> / name

echo "$replace"

返回

.TAG name
result input1 random_input / name1
random_input<10> input1 another_input / name2
.NTAG

现在我只需要用 $replace 替换 $find。我试过 sed 和 perl 但都失败了。

尝试过(在很多其他的东西中)

perl -e -i.bak "s/$find/$replace/g" text.file
perl -e -i.bak 's,"'$find'","'$replace'",g' text.file
perl -e -i.bak "s|$find|$replace|g" text.file

sed -i "s/$find/$replace/g" text.file

我的猜测是问题是由字符串中的某些字符被解释为特殊字符引起的。

感谢任何帮助!

【问题讨论】:

  • 不完全是,问题是当您要操作的是字符串时,您正在使用正则表达式构造。你不能用 sed 做到这一点,因为它只能在正则表达式上运行,我希望 perl 确实有一些构造来处理字符串,但那些不是你正在使用的构造。

标签: regex perl shell awk sed


【解决方案1】:

这适用于查找或替换的任何值:

$ cat file
foo
type result input1 another_input random_input<10> / name
bar

$ awk -v find="$find" -v replace="$replace" 's=index($0,find){$0=substr($0,1,s-1) replace substr($0,s+length(find))}1' file
foo
.TAG name
result input1 random_input / name1
random_input<10> input1 another_input / name2
.NTAG
bar

如果find 总是一整行,则可以简化,例如这可能就是您所需要的:

$ awk -v find="$find" -v replace="$replace" '$0==find{$0=replace}1' file
foo
.TAG name
result input1 random_input / name1
random_input<10> input1 another_input / name2
.NTAG
bar

【讨论】:

  • 非常感谢您的解决方案。你有没有机会解释使用的语法或指向我的参考?
  • man awk 并查找 index() 和 substr() 函数。此外,我强烈推荐 Arnold Robbins 的《Effective Awk Programming》一书。
  • @Ed Morton:我只是在我的 AIX (KSH) 上尝试使用特殊字符 find='$1';replace='$2' 并没有替换 $2 中的$1。您认为这是由于我的 shell 行为还是 sed 中 awk 的限制?
  • 这不是 awk 的限制,我知道的任何 shell 也不会有任何问题。字符串中的这些字符没有什么特别之处。您是否使用这些语句设置 awk 变量或 shell 变量?您是真的希望find 具有值$1(即一个美元符号,然后是数字1)还是希望它包含外壳的第一个位置参数的值(在这种情况下,您' d 必须使用带有双引号的find="$1",而不是单引号,以允许外壳扩展$1) 或其他什么?
  • 我将文字 $1(以及用于测试的 $2)放在 texte 文件的一行中,并尝试使用 find 的值作为显示(看起来是文字 $1 而不是内容的第一个最终论点)。很感兴趣,因为这很难在 sed 中处理所有这种特殊的字符,并且需要在 sed 本身中使用之前准备好值。
【解决方案2】:

重组你的变量来读取

replace=".TAG name\nresult input1 random_input / name1\nrandom_input<10> input1 another_input / name2\n.NTAG"

或者像这样重组它

replace='.TAG name\
    result input1 random_input / name1\
    random_input<10> input1 another_input / name2\
    .NTAG'

此外,您的分隔符与您的变量 replace 字符冲突,因为您的变量中有 /

您可以尝试将# 作为您的分隔符或任何其他不在您的变量中的字符

 sed "s#$find#$replace#g" text_file

【讨论】:

  • 根据find 和/或replace 的内容,有几十种方法可能会失败。你不能用 sed 稳健地做到这一点。
【解决方案3】:

一种使用 sed 的方式,快速而肮脏,因此不像 @Ed Morton 的 awk 那样防弹

find_sed="$( printf "%s" "${find}" | sed 's/[^0-9(){}]/\\&/g' )"
replace_sed="$( printf "%s" "${replace}" | sed "s/[&\\]/\\/g;s/\n/\\&/g' )"

sed "s/${find_sed}/${replace_sed}/g" YourFile
  • 可以是带有内部替换而不是变量的单行代码,但更明确的是这样
  • posix 版本(--posix 与 GNU sed),如果不是 posix,则根据您使用转义字符的特殊字符调整 [^0-9(){}],例如 \+\n\d\

【讨论】:

  • find 和/或 replace 的许多值会导致脚本失败,要么在应该替换 find 时不替换它,要么用错误的东西替换它,或者通过产生语法错误。我不确定只是说它不是防弹的就可以捕捉到它的脆弱程度,不幸的是它可能会为一些样本输入集产生预期的输出,OP 对其进行测试,所以他可能认为这是一个强大的解决方案然后去在他们的真实数据上执行它并完全搞砸。
  • 完全同意,除了直接特殊字符之外没有任何安全性。安全化将取决于几个参数(操作系统/销售、价值来源),但对于提供的样本等数据而言,它不会出现很多问题。
猜你喜欢
  • 2014-01-04
  • 1970-01-01
  • 2014-05-24
  • 2014-12-06
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 2014-01-09
  • 2023-01-11
相关资源
最近更新 更多