【发布时间】:2011-11-04 02:45:08
【问题描述】:
假设我有两个字符串列表(列表 A 和列表 B),每个列表中的条目数 N 完全相同,我想用 A 的第 n 个元素替换所有出现的 A 的第 n 个元素B 在 Unix 中的文件中(最好使用 Bash 脚本)。
最有效的方法是什么?
一种低效的方法是对“sed s/stringA/stringB/g”进行 N 次调用。
【问题讨论】:
标签: bash unix scripting file-io
假设我有两个字符串列表(列表 A 和列表 B),每个列表中的条目数 N 完全相同,我想用 A 的第 n 个元素替换所有出现的 A 的第 n 个元素B 在 Unix 中的文件中(最好使用 Bash 脚本)。
最有效的方法是什么?
一种低效的方法是对“sed s/stringA/stringB/g”进行 N 次调用。
【问题讨论】:
标签: bash unix scripting file-io
这将一次性完成。它将 listA 和 listB 读入 awk 数组,然后对于 linput 的每一行,它检查每个单词,如果在 listA 中找到该单词,则将该单词替换为 listB 中的相应单词。
awk '
FILENAME == ARGV[1] { listA[$1] = FNR; next }
FILENAME == ARGV[2] { listB[FNR] = $1; next }
{
for (i = 1; i <= NF; i++) {
if ($i in listA) {
$i = listB[listA[$i]]
}
}
print
}
' listA listB filename > filename.new
mv filename.new filename
我假设 listA 中的字符串不包含空格(awk 的默认字段分隔符)
【讨论】:
substr 函数查找要替换的单词前后的行部分,并将这些部分与替换连接起来。或者用 perl 重写,这样更容易维护分隔符
调用编写 sed 脚本的sed,然后再调用一次来使用它?如果您的列表在文件listA 和listB 中,那么:
paste -d : listA listB | sed 's/\([^:]*\):\([^:]*\)/s%\1%\2%/' > sed.script
sed -f sed.script files.to.be.mapped.*
我正在对不包含冒号或百分号的“单词”做出一些全面的假设,但您可以适应这一点。某些版本的sed 对可以指定的命令数量有上限;如果这是一个问题,因为您的单词列表足够大,那么您可能必须将生成的 sed 脚本拆分为单独的文件,这些文件将被应用 - 或者更改为使用没有限制的东西(例如 Perl)。
另一个需要注意的是变化的顺序。如果要交换两个单词,则需要仔细制作单词列表。一般来说,如果您将 (1) wordA 映射到 wordB 以及 (2) wordB 到 wordC,那么 sed 脚本在映射 (2) 之前还是之后进行映射 (1) 很重要。
显示的脚本不注意单词边界;您可以通过多种方式对它们进行谨慎处理,具体取决于您使用的sed 版本以及您对单词构成的标准。
【讨论】:
sed 进行编码,在每次替换操作后使用“t”命令。
我需要做一些类似的事情,最后我根据地图文件生成了 sed 命令:
$ cat file.map
abc => 123
def => 456
ghi => 789
$ cat stuff.txt
abc jdy kdt
kdb def gbk
qng pbf ghi
non non non
try one abc
$ sed `cat file.map | awk '{print "-e s/"$1"/"$3"/"}'`<<<"`cat stuff.txt`"
123 jdy kdt
kdb 456 gbk
qng pbf 789
non non non
try one 123
确保您的 shell 支持与地图中一样多的 sed 参数。
【讨论】:
sed & bash 版本:sed -f <(sed 's/=> //;s# #/#;s#$#/#;s#^#s/#' file.map) stuff.txt。
这对于 Tcl 来说相当简单:
set fA [open listA r]
set fB [open listB r]
set fin [open input.file r]
set fout [open output.file w]
# read listA and listB and create the mapping of corresponding lines
while {[gets $fA strA] != -1} {
set strB [gets $fB]
lappend map $strA $strB
}
# apply the mapping to the input file
puts $fout [string map $map [read $fin]]
# if the file is large, do it line by line instead
#while {[gets $fin line] != -1} {
# puts $fout [string map $map $line]
#}
close $fA
close $fB
close $fin
close $fout
file rename output.file input.file
【讨论】:
您可以在bash 中执行此操作。将您的列表放入数组中。
listA=(a b c)
listB=(d e f)
data=$(<file)
echo "${data//${listA[2]}/${listB[2]}}" #change the 3rd element. Redirect to file where necessary
【讨论】:
使用 tr(1)(翻译或删除字符):
cat file | tr 'abc' 'XYZ' > file_new
mv file_new file
【讨论】: