Bash:使用另一个文件的行查找并替换文件中的行
我有两个文件: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如下所示: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
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,不是字面意义;我也理解你的是省略号。