Bash:使用另一个文件的行查找并替换文件中的行

Bash:使用另一个文件的行查找并替换文件中的行,bash,awk,sed,replace,file-io,Bash,Awk,Sed,Replace,File Io,我有两个文件:masterlist.txt有数百行URL,toupdate.txt有少量需要替换的masterlist.txt文件中的更新版本 我希望能够使用Bash自动化这个过程,因为这些列表的创建和利用已经在Bash脚本中发生 URL的服务器部分是更改的部分,因此我们可以使用唯一的部分:/which/which_user.xml进行匹配,但是如何在masterlist.txt中查找和替换这些行呢?i、 e.如何遍历toupdate.txt的每一行,当它以/f_SomeName/f_SomeN

我有两个文件:masterlist.txt有数百行URL,toupdate.txt有少量需要替换的masterlist.txt文件中的更新版本

我希望能够使用Bash自动化这个过程,因为这些列表的创建和利用已经在Bash脚本中发生

URL的服务器部分是更改的部分,因此我们可以使用唯一的部分:/which/which_user.xml进行匹配,但是如何在masterlist.txt中查找和替换这些行呢?i、 e.如何遍历toupdate.txt的每一行,当它以/f_SomeName/f_SomeName\u user.xml结尾时,找到以masterlist.txt结尾的那一行,并用新的一行替换整行

所以https://123456url.domain.com/26/path/f_SomeName/f_SomeName_user.xml 变成https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml 比如说

masterlist.txt的其余部分需要保持完整,因此我们必须只查找和替换具有相同行尾ID的不同服务器的行

结构 masterlist.txt如下所示:

https://123456url.domain.com/26/path/f_SomeName/f_SomeName_user.xml
https://456789url.domain.com/32/path/f_AnotherName/f_AnotherName_user.xml
https://101112url.domain.com/1/path/g_SomethingElse/g_SomethingElse_user.xml
https://222blah11.domain.com/19/path/e_BlahBlah/e_BlahBlah_user.xml
[...]
https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml
https://foo-254.domain.com/8/path/g_SomethingElse/g_SomethingElse_user.xml
toupdate.txt如下所示:

https://123456url.domain.com/26/path/f_SomeName/f_SomeName_user.xml
https://456789url.domain.com/32/path/f_AnotherName/f_AnotherName_user.xml
https://101112url.domain.com/1/path/g_SomethingElse/g_SomethingElse_user.xml
https://222blah11.domain.com/19/path/e_BlahBlah/e_BlahBlah_user.xml
[...]
https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml
https://foo-254.domain.com/8/path/g_SomethingElse/g_SomethingElse_user.xml
期望结果 使masterlist.txt看起来像:

https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml
https://456789url.domain.com/32/path/f_AnotherName/f_AnotherName_user.xml
https://foo-254.domain.com/8/path/g_SomethingElse/g_SomethingElse_user.xml
https://222blah11.domain.com/19/path/e_BlahBlah/e_BlahBlah_user.xml
[...]
初步检查 我已经看过sed,但是我不知道如何使用这两个文件中的行进行查找和替换

以下是我到目前为止所做的工作,至少是处理文件:

#!/bin/bash

#...

while read -r line; do
    # there's a new link on each line
    link="${line}"
    # extract the unique part from the end of each line
    grabXML="${link##*/}"
    grabID="${grabXML%_user.xml}"
    # if we cannot grab the ID, then just set it to use the full link so we don't have an empty string
    if [ -n "${grabID}" ]; then
        identifier=${grabID}
    else
        identifier="${line}"
    fi
    
    ## the find and replace here? ##    

# we're done when we've reached the end of the file
done < "masterlist.txt"

请您尝试以下方法:

#!/bin/bash

declare -A map
while IFS= read -r line; do
    if [[ $line =~ (/[^/]+/[^/]*\.xml)$ ]]; then
        uniq_part="${BASH_REMATCH[1]}"
        map[$uniq_part]=$line
    fi
done < "toupdate.txt"

while IFS= read -r line; do
    if [[ $line =~ (/[^/]+/[^/]*\.xml)$ ]]; then
        uniq_part="${BASH_REMATCH[1]}"
        if [[ -n ${map[$uniq_part]} ]]; then
            line=${map[$uniq_part]}
        fi
    fi
    echo "$line"
done < "masterlist.txt" > "masterlist_tmp.txt"

# if the result of "masterlist_tmp.txt" is good enough, uncomment the line below
# mv -f -- "masterlist_tmp.txt" "masterlist.txt"
[解释]

关联数组映射将唯一部分(如/f_SomeName/f_SomeName_user.xml)映射到完整路径(如https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml. regex/[^/]+/[^/]*\.xml$如果匹配,则分配shell变量 BASH_将[1]从最右边的第二个斜杠重新匹配到子字符串 到字符串末尾的extention.xml。 在文件toupdate.txt的第一个循环中,它生成唯一的零件 并将路径对填充为关联数组的键值对。 在masterlist.txt文件的第二个循环中,提取 如果存在关联值,则测试唯一零件。若有,详情为何? 行被相关值替换,即toupdate.txt中的行 文件 [备选案文] 如果文本文件很大,bash可能不够快。在这种情况下,awk脚本将更有效地工作:

awk 'NR==FNR {
    if (match($0, "/[^/]+/[^/]*\\.xml$")) {
        map[substr($0, RSTART, RLENGTH)] = $0
    }
    next
}
{
    if (match($0, "/[^/]+/[^/]*\\.xml$")) {
        full_path = map[substr($0, RSTART, RLENGTH)]
        if (full_path != "") {
            $0 = full_path
        }
    }
    print
}' "toupdate.txt" "masterlist.txt" > "masterlist_tmp.txt"
[解释]

NR==FNR{BLOCK1;next}{BLOCK2}语法是 为每个文件分别切换处理。作为NR==FNR条件 仅满足参数列表中的第一个文件,下一个语句跳过 下面的块BLOCK1仅处理文件toupdate.txt。 类似地,BLOCK2仅处理masterlist.txt文件。 如果函数匹配$0,则模式成功,它将设置awk变量 r从$0开始到匹配子字符串的起始位置, 从文件中读取的当前记录, 然后将变量RLENGTH设置为匹配子字符串的长度。 现在我们可以提取匹配的子字符串,如 /f_SomeName/f_SomeName_user.xml,方法是使用substr函数。 然后我们分配数组映射,使子字符串成为唯一的部分 映射到toupdate.txt中的整个url。 第二个区块的工作原理与第一个区块基本相似。如果该键对应的值 在数组映射中找到,然后将记录$0替换为 由键索引的数组的值。
请您尝试以下方法:

#!/bin/bash

declare -A map
while IFS= read -r line; do
    if [[ $line =~ (/[^/]+/[^/]*\.xml)$ ]]; then
        uniq_part="${BASH_REMATCH[1]}"
        map[$uniq_part]=$line
    fi
done < "toupdate.txt"

while IFS= read -r line; do
    if [[ $line =~ (/[^/]+/[^/]*\.xml)$ ]]; then
        uniq_part="${BASH_REMATCH[1]}"
        if [[ -n ${map[$uniq_part]} ]]; then
            line=${map[$uniq_part]}
        fi
    fi
    echo "$line"
done < "masterlist.txt" > "masterlist_tmp.txt"

# if the result of "masterlist_tmp.txt" is good enough, uncomment the line below
# mv -f -- "masterlist_tmp.txt" "masterlist.txt"
[解释]

关联数组映射将唯一部分(如/f_SomeName/f_SomeName_user.xml)映射到完整路径(如https://new-123.domain.com/1/path/f_SomeName/f_SomeName_user.xml. regex/[^/]+/[^/]*\.xml$如果匹配,则分配shell变量 BASH_将[1]从最右边的第二个斜杠重新匹配到子字符串 到字符串末尾的extention.xml。 在文件toupdate.txt的第一个循环中,它生成唯一的零件 并将路径对填充为关联数组的键值对。 在masterlist.txt文件的第二个循环中,提取 如果存在关联值,则测试唯一零件。若有,详情为何? 行被相关值替换,即toupdate.txt中的行 文件 [备选案文] 如果文本文件很大,bash可能不够快。在这种情况下,awk脚本将更有效地工作:

awk 'NR==FNR {
    if (match($0, "/[^/]+/[^/]*\\.xml$")) {
        map[substr($0, RSTART, RLENGTH)] = $0
    }
    next
}
{
    if (match($0, "/[^/]+/[^/]*\\.xml$")) {
        full_path = map[substr($0, RSTART, RLENGTH)]
        if (full_path != "") {
            $0 = full_path
        }
    }
    print
}' "toupdate.txt" "masterlist.txt" > "masterlist_tmp.txt"
[解释]

NR==FNR{BLOCK1;next}{BLOCK2}语法是 为每个文件分别切换处理。作为NR==FNR条件 仅满足参数列表中的第一个文件,下一个语句跳过 下面的块BLOCK1仅处理文件toupdate.txt。 类似地,BLOCK2仅处理masterlist.txt文件。 如果函数匹配$0,则模式成功,它将设置awk变量 r从$0开始到匹配子字符串的起始位置, 从文件中读取的当前记录, 然后将变量RLENGTH设置为匹配子字符串的长度。 现在我们可以提取匹配的子字符串,如 /f_SomeName/f_SomeName_user.xml,方法是使用substr函数。 这个 n我们分配数组映射,使子字符串成为唯一部分 映射到toupdate.txt中的整个url。 第二个区块的工作原理与第一个区块基本相似。如果该键对应的值 在数组映射中找到,然后将记录$0替换为 由键索引的数组的值。 为什么sed不编写自己的脚本—生成所需的输出

sed -e "$(sed -e 's<^\(http[s]*://[^/]*/[^/]*/\)\(.*\)<\\|\2\$| s|.*|\1\2|<' toupdate.txt)" masterlist.txt
在哪里

内部sed命令有一个外部替换命令和一个内部替换命令 外部s为什么sed不编写自己的脚本—生成所需的输出

sed -e "$(sed -e 's<^\(http[s]*://[^/]*/[^/]*/\)\(.*\)<\\|\2\$| s|.*|\1\2|<' toupdate.txt)" masterlist.txt
在哪里

内部sed命令有一个外部替换命令和一个内部替换命令
我喜欢awk的答案!谢谢。你能再解释一下awk答案是如何工作的吗?@nooblag查看类似的awk命令的解释。谢谢你的反馈。我在我的答案中添加了对awk脚本的小解释。我喜欢awk的答案!谢谢。你能再解释一下awk答案是如何工作的吗?@nooblag查看类似的awk命令的解释。谢谢你的反馈。我已经在我的回答中添加了关于awk脚本的小说明。谢谢。如果我理解正确,我应该注意到。。。不是文字,我在masterlist.txt的摘录中使用了省略号来表示等等。我将添加一些括号来帮助澄清这一点?@nooblag是的。在我的解释中我用了。。。而不是省略号U+2026,不是字面意义;我也明白你的话是省略号,谢谢。如果我理解正确,我应该注意到。。。不是文字,我在masterlist.txt的摘录中使用了省略号来表示等等。我将添加一些括号来帮助澄清这一点?@nooblag是的。在我的解释中我用了。。。而不是省略号U+2026,不是字面意义;我也理解你的是省略号。