【问题标题】:replace string inside `<value> REPLACE ANY STRING</value>` for solaris 10 UNIX command为 solaris 10 UNIX 命令替换 `<value> REPLACE ANY STRING</value>` 中的字符串
【发布时间】:2021-07-27 06:58:48
【问题描述】:

我想使用从 source.xml 中提取的值替换 target.xml 中 &lt;value&gt;{Change_me}&lt;/value&gt; 中的字符串 {Change_me}

  1. 以下是我的文件
source.xml
    <name>App1</name>
    <value>#!@+aw13dawe=</value>
    <name>App2</name>
    <value>=313ak#a!@BAd</value>
    <name>App3</name>
    <value>!23aaB8=l6</value>
    <name>App4</name>
    <value>0913@aa!#=</value>

target.xml
    <name>App1</name>
    <value>{Change_me}</value>
    <name>App2</name>
    <value>{Change_me}</value>
    <name>App3</name>
    <value>{Change_me}</value>
    <name>App4</name>
    <value>{Change_me}</value>

    forloop file list.txt
    App1
    App2
    App3
    App4

script1(在 sed 下面的工作在 linux 系统中,但在 SunOS 未知 5.10 Generic_147148-26 中不工作 i86pc i386 i86pc)

    #!/bin/ksh
    a={Change_me}

    for i in $(cat list.txt) ;
        do sed -i.bak "0,/$a/s//$(grep $i source.xml -A1 | grep value | grep -oP '(?<=value>).*?(?=</value>)')/" target.xml;
    done

我在 Solaris 中编写的 Script2 遇到了问题。

    #!/bin/ksh
    a={Change_me}

    for i in $(cat list.txt) ;
        do sed -e "0,/"$a"/s//"$(/usr/sfw/bin/ggrep $i -A1 source.xml | grep value | sed -e 's/.*<value>\(.*\)<\/value>.*/\1/')"" target.xml ;
    done

结果:

# ksh -x change.ksh
/bin/pwd
2> /dev/null
PWD=/scripts/middleware
+ a={Change_me}
+ cat list.txt
+ /usr/sfw/bin/ggrep App1 -A1 source.xml
+ grep value
+ sed -e s/.*<value>\(.*\)<\/value>.*/\1/
+ sed -e 0,/{Change_me}/s//#!@+aw13dawe= target.xml
sed: command garbled: 0,/{Change_me}/s//#!@+aw13dawe=
+ /usr/sfw/bin/ggrep App2 -A1 source.xml
+ grep value
+ sed -e s/.*<value>\(.*\)<\/value>.*/\1/
+ sed -e 0,/{Change_me}/s//=313ak#a!@BAd target.xml
sed: command garbled: 0,/{Change_me}/s//=313ak#a!@BAd
+ /usr/sfw/bin/ggrep App3 -A1 source.xml
+ grep value
+ sed -e s/.*<value>\(.*\)<\/value>.*/\1/
+ sed -e 0,/{Change_me}/s//!23aaB8=l6 target.xml
sed: command garbled: 0,/{Change_me}/s//!23aaB8=l6
+ /usr/sfw/bin/ggrep App4 -A1 source.xml
+ grep value
+ sed -e s/.*<value>\(.*\)<\/value>.*/\1/
+ sed -e 0,/{Change_me}/s//0913@aa!#= target.xml
sed: command garbled: 0,/{Change_me}/s//0913@aa!#=

sed -i 在 solaris 中不起作用,这就是我使用 sed -e 的原因,但正如结果所示,它没有按预期工作,非常感谢您的帮助和建议。

这是一个很好的案例输出注意:仅适用于 Linux (RHEL 7 - testmachine)

[root@vmserver1 ~]# bash -x change.sh
+ a='{Change_me}'
++ cat list.txt
+ for i in '$(cat list.txt)'
++ grep App1 source.xml -A1
++ grep value
++ grep -oP '(?<=value>).*?(?=</value>)'
+ sed -i.bak '0,/{Change_me}/s//#!@+aw13dawe=/' target.xml
+ for i in '$(cat list.txt)'
++ grep App2 source.xml -A1
++ grep value
++ grep -oP '(?<=value>).*?(?=</value>)'
+ sed -i.bak '0,/{Change_me}/s//=313ak#a!@BAd/' target.xml
+ for i in '$(cat list.txt)'
++ grep App3 source.xml -A1
++ grep value
++ grep -oP '(?<=value>).*?(?=</value>)'
+ sed -i.bak '0,/{Change_me}/s//!23aaB8=l6/' target.xml
+ for i in '$(cat list.txt)'
++ grep App4 source.xml -A1
++ grep value
++ grep -oP '(?<=value>).*?(?=</value>)'
+ sed -i.bak '0,/{Change_me}/s//0913@aa!#=/' target.xml
[root@vmserver1 ~]#

target.xml 的良好结果

[root@vmserver1 ~]# cat target.xml
<name>App1</name>
<value>#!@+aw13dawe=</value>
<name>App2</name>
<value>=313ak#a!@BAd</value>
<name>App3</name>
<value>!23aaB8=l6</value>
<name>App4</name>
<value>0913@aa!#=</value>

【问题讨论】:

  • 请显示ksh -x change.ksh 的输出,无论是好的情况还是坏的情况。解释你的算法也可能会有所帮助。我是否正确理解您正在尝试提取&lt;value&gt;&lt;/value&gt; 之间的字符串并构造一个sed 命令,该命令将用{Change_me} 完全替换该字符串?文件source.xml 是否完整或您的真实文件是否包含更多数据?在后一种情况下,显示一个更好地代表您的真实输入的示例。请edit回答您的问题。
  • 我认为ksh -x 输出仅显示坏情况。最好也有好的案例来比较
  • 嗨,Bodo,糟糕的情况在帖子中,我的主要目标是我想替换源 target.xml 中存在的&lt;value&gt;{Change_me}&lt;/value&gt; 的标签内的字符串{Change_me}。 xml 是我替换 {Change_me} 的输入变量,因为您注意到每个名称都有一个分配的值,这就是为什么我使用 grep -A1 根据名称分配适当的值,就像从源复制适当的密码一样。 xml 到 target.xml。我希望我的解释很清楚,稍后会发布好的案例,以便我们更好地理解预期的输出。非常感谢。
  • 您好 Bodo,我在帖子中添加了好案例,因为无法在评论中添加,非常感谢。
  • Do not parse xml with regex。是否要将 &lt;name&gt;{this} 与 source.xml 和 target.xml 文件之间的匹配进行匹配?或者您是否只想将 target.xml 中的第一个 {change} 替换为 source.xml 文件中的第一个 ,第二个替换为第二个等?。

标签: shell unix ksh solaris-10


【解决方案1】:

当您查看sed -e 0,/{Change_me}/s//#!@+aw13dawe= target.xml 时,您会注意到您缺少结尾的/
所以改变

sed -e "0,/"$a"/s//"$(/usr/sfw/bin/ggrep $i -A1 source.xml |
  grep value |
  sed -e 's/.*<value>\(.*\)<\/value>.*/\1/')"" target.xml

进入

sed -e "0,/"$a"/s//"$(/usr/sfw/bin/ggrep $i -A1 source.xml |
  grep value |
  sed -e 's/.*<value>\(.*\)<\/value>.*/\1/')"/" target.xml

但是,这会因密码奇怪而失败,当可能出现随机字符串时,不要使用sed 生成sed 命令。
当您想要一个带有sed 而不是 xml 解析器的解决方案时,您将不得不与所有需要大量修改的小异常作斗争。在短时间内,您可能会限制可接受的密码并使用上述解决方案,这可能会让您有时间寻找更好的工具。
在您的解决方案中,您还必须确保 list.txt 的所有条目与 {change_me} 的顺序与 target.xml 相同。
当您仍然不想学习 xml 浏览器时,可以考虑使用awk。使用awk,您可以读取 2 个文件,使用名称/值创建一个数组,然后将该数组用于第二个。尝试一下,当您遇到困难时发布一个新问题。

【讨论】:

  • 嗨@Walter A,我不确定我还缺少什么,它没有引发错误,但我的文件没有更改这里是一些输出# ksh -x change.ksh /bin/pwd 2&gt; /dev/null PWD=/scripts/middleware + a={Change_me} + cat list.txt + /usr/sfw/bin/ggrep App1 -A1 source.xml + grep value + sed -e s/.*&lt;value&gt;\(.*\)&lt;\/value&gt;.*/\1/ + sed -e 0,/{Change_me}/s//#!@+aw13dawe=/ target.xml &lt;name&gt;App1&lt;/name&gt; &lt;value&gt;{Change_me}&lt;/value&gt; &lt;name&gt;App2&lt;/name&gt;
  • 如果没有帮助,我将删除此答案。尝试将您的命令拆分为更小的步骤,并查看哪个步骤失败了。也许使用printf "%s\n" {7..14} | sed '0,/1/ s//changed /' 进行一些基本测试。
  • 嗨@Walter A,似乎无法在 Solaris 上运行,这是输出 # printf "%s\n" {7..14} | sed '0,/1/ s//changed /' {7..14} # uname -a SunOS unknown 5.10 Generic_147148-26 i86pc i386 i86pc,但适用于 linux [root@vmserver1 ~]# printf "%s\n " {1..4} | sed '0,/1/s//改变/'改变2 3 4
  • 与 Solaris 上的 sed 版本似乎不同,也许 printf "%s\n" {7..14} | sed '0,/1/ s/1/changed /'(我在 sed 命令中添加了 1)可以工作。在尝试修复完整的解决方案之前先测试一下 (sed -e "0,/"$a"/s/"$a"/"...)
  • 嗨@Walter A,我想我需要在 Solaris 中放弃这个 sed :),我尝试了建议的命令但不起作用,我通过更改 sed -e "0 to 1" 尝试了不同的方法我发现这个数字在匹配模式之前首先被解释,这意味着如果我从索引 0 开始并且没有找到模式,它将不会扫描下一个索引,这就是为什么没有输出更改,所以当我将它更改为 1 时能够替换文本,但由于这是一个使用 list.txt 的 for 循环,它只会重复更改 App1 值,我同意 KamilCuk 使用 sed 解析 xml 不是一个好主意。
【解决方案2】:

以下带有 cmets 的 POSIX 脚本公然忽略输入文件具有 XML 格式并强烈假定文件的特定格式。假设这些文件是基于行的,它们由一行一行的&lt;name&gt;something&lt;/name&gt;&lt;value&gt;something&lt;/value&gt; 组成。

#!/bin/sh

cat > source.xml <<EOF
    <name>App1</name>
    <value>#!@+aw13dawe=</value>
    <name>App2</name>
    <value>=313ak#a!@BAd</value>
    <name>App3</name>
    <value>!23aaB8=l6</value>
    <name>App4</name>
    <value>0913@aa!#=</value>
EOF
cat > target.xml <<EOF
    <name>App1</name>
    <value>{Change_me}</value>
    <name>App2</name>
    <value>{Change_me}</value>
    <name>App3</name>
    <value>{Change_me}</value>
    <name>App4</name>
    <value>{Change_me}</value>
EOF

# Load target to memory.
data=$(cat target.xml)

# Let's preprocess source.xml to a CSV file separated with tabs
# This assumes strict format of source.xml
# I am using space for s command separator.
source=$(sed 'N;s .*<name>\(.*\)</name>.*<value>\(.*\)</value>.* \1\t\2 ' source.xml)
# For each name
data=$(
    printf "%s\n" "$source" | {
        while IFS=$(printf '\t') read -r name value; do
            # Find the line number taht contains the name tag
            if ! nameline=$(
                     printf "%s\n" "$data" |
                     grep -nF "<name>$name</name>" |
                     cut -d: -f1
                   ) || [ -z "$nameline" ]; then
                # Handle name not found
                continue
            fi
            # We are strongly assuming we are editing the next line.
            editline=$((nameline + 1))
            # Escape sed pattern.
            # https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
            valueesc=$(printf "%s\n" "$value" | sed -e 's/[\/&]/\\&/g')
            # edit the line with sed.
            data=$(printf "%s\n" "$data" | sed "$editline"'s \(<value>\)[^>]*\(</value>\) \1'"$valueesc"'\2 ')
        done
        printf "%s\n" "$data"
    }
)

# Display modified file content
printf "%s\n" "$data"

脚本输出:

    <name>App1</name>
    <value>#!@+aw13dawe=</value>
    <name>App2</name>
    <value>=313ak#a!@BAd</value>
    <name>App3</name>
    <value>!23aaB8=l6</value>
    <name>App4</name>
    <value>0913@aa!#=</value>

【讨论】:

    【解决方案3】:

    awk 的解决方案可能适用于您的情况:

    awk -F '[<>]' '
      NR==FNR && $2 == "name" { lastname=$3 }
      NR==FNR && $2 == "value" { pw[lastname]=$3 }
      NR>FNR && $2 == "name" { lastname=$3; print }
      NR>FNR && $2 == "value" { printf("<value>%s</value>\n", pw[lastname]) }
      ' source.xml target.xml
    

    当您有其他愿望时,请尝试更改脚本并在遇到困难时发布新问题。可能的改进是:

    • 仅在$3 == "{Change_me}" 时更改值
    • (复杂)首先阅读list.txt,只有在更改[]中找到名称时才更改值。
    • 重新设计,让您可以在一行中有不同的标签。
    • 修复了&lt;&gt; 密码会失败的问题(检查NF)。

    【讨论】:

      猜你喜欢
      • 2019-07-06
      • 2019-09-08
      • 2017-03-06
      • 2020-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多