Bash 为多个sed替换优化shell脚本
我有一个文件,其中包含替换对的列表(大约100个),sed使用这些替换对替换文件中的字符串 这对像:Bash 为多个sed替换优化shell脚本,bash,shell,sed,Bash,Shell,Sed,我有一个文件,其中包含替换对的列表(大约100个),sed使用这些替换对替换文件中的字符串 这对像: old|new tobereplaced|replacement (stuffiwant).*(too)|\1\2 我现在的代码是: cat replacement_list | while read i do old=$(echo "$i" | awk -F'|' '{print $1}') #due to the need for extended regex new
old|new
tobereplaced|replacement
(stuffiwant).*(too)|\1\2
我现在的代码是:
cat replacement_list | while read i
do
old=$(echo "$i" | awk -F'|' '{print $1}') #due to the need for extended regex
new=$(echo "$i" | awk -F'|' '{print $2}')
sed -r "s/`echo "$old"`/`echo "$new"`/g" -i file
done
我忍不住认为有一种更为理想的替换方式。我试着把循环转过来,先遍历文件的行,但结果是成本要高得多
有没有其他加速脚本的方法
编辑
感谢所有的快速回复。在选择答案之前,让我先尝试一下各种建议
有一件事需要澄清:我还需要子表达式/组功能。例如,我可能需要的一个替代品是:
([0-9])U|\10 #the extra brackets and escapes were required for my original code
有关改进的一些详细信息(待更新):
- 方法:处理时间
- 原始脚本:0.85s
代替cut
:0.71sawk
- 阿努巴瓦法:0.18s
- chthonicdaemon法:0.01s
sed-e
,以及一个可能不那么广为人知的MySQL命令行实用程序,replace
。为字符串替换进行优化几乎比sed快一个数量级。结果如下所示(最慢优先):
如果您想要性能,请使用。不过,要在您的系统上使用它,您需要安装一些MySQL发行版
发件人:
替换文本文件中的字符串
此程序将替换文件中的字符串或从stdin到stdout的字符串。它接受从字符串/到字符串对的列表,并用对应的to字符串替换from字符串的每次出现。找到的字符串的第一个匹配项。如果替换字符串的可能性不止一种,则在替换较短的匹配之前,首选较长的匹配
程序对字符串进行DFA状态机,速度不取决于替换字符串的数量(仅取决于替换的数量)。假定一行以\n或\0结尾。字符串的长度没有超出内存的限制
更多关于sed。通过将替换件拆分为#CPU组,然后通过
sed
命令对其进行管道传输,您可以在sed中使用多个内核,如下所示:
$ sed -e 's/A/B/g; ...' file.txt | \
sed -e 's/B/C/g; ...' | \
sed -e 's/C/D/g; ...' | \
sed -e 's/D/E/g; ...' > out
# first, split my replace-list into manageable chunks (89 files in this case)
split -a 4 -l 1000 80kReplacePairs rep_
# next, make a 'replace' script out of each chunk
for F in rep_* ; do \
echo "create and make executable a scriptfile" ; \
echo '#!/bin/sh' > run_$F.sh ; chmod +x run_$F.sh ; \
echo "for each chunk-file line, strip line-ends," ; \
echo "then with sed, turn '{long list}' into 'cat in | {long list}' > out" ; \
cat $F | tr '\n' ' ' | sed 's/^/cat in | replace /;s/$/ > out/' >> run_$F.sh ;
echo "and append commands to switch in and out files, for next script" ; \
echo -e " && \\\\ \nrm in && mv out in\n" >> run_$F.sh ; \
done
# put all the replace-scripts in sequence into a main script
ls ./run_rep_aa* > allrun.sh
# make it executable
chmod +x allrun.sh
# run it
nohup ./allrun.sh &
此外,如果您使用sed
或perl
,并且您的系统具有UTF-8设置,那么在命令前面放置LANG=C
也会提高性能:
$ LANG=C sed ...
您可以减少不必要的awk调用,并使用BASH断开名称-值对:
while IFS='|' read -r old new; do
# echo "$old :: $new"
sed -i "s~$old~$new~g" file
done < replacement_list
而IFS='|'read-r old new;做
#echo“$old::$new”
sed-i“s~$old~$new~g”文件
完成<更换列表
IFS=“|”将在两个不同的shell变量old
和new
中提供enable read来填充名称值
这是假设您的名称-值对中不存在
~
。如果情况并非如此,则可以随意使用备用的sed分隔符。您可以使用sed
生成格式正确的sed
输入:
sed -e 's/^/s|/; s/$/|g/' replacement_list | sed -r -f - file
你可以试试这个
pattern=''
cat replacement_list | while read i
do
old=$(echo "$i" | awk -F'|' '{print $1}') #due to the need for extended regex
new=$(echo "$i" | awk -F'|' '{print $2}')
pattern=${pattern}"s/${old}/${new}/g;"
done
sed -r ${pattern} -i file
这将在包含所有替换项的文件上仅运行一次sed命令。您可能还想将awk
替换为cut
cut
可能比awk
更优化,尽管我不确定这一点
old=`echo $i | cut -d"|" -f1`
new=`echo $i | cut -d"|" -f2`
您可能希望在awk中完成整个过程:
awk -F\| 'NR==FNR{old[++n]=$1;new[n]=$2;next}{for(i=1;i<=n;++i)gsub(old[i],new[i])}1' replacement_list file
awk-F\|'NR==FNR{old[++n]=$1;new[n]=$2;next}{for(i=1;i以下是我将尝试的:
将您的sed
search-replace对存储在Bash数组中,如下所示:
使用以下命令基于此阵列生成sed命令
运行命令
模式=(
新旧
置换
)
模式计数=${#模式[*]}#模式数
sedArgs=()#将保存sed参数列表
对于((i=0;i
通过sed编写代码更有趣。请尝试一次性能测试,因为这只启动1个sed,这是递归的
对于posix sed(带GNU sed的so--posix
)
解释
- 使用分隔符复制文件内容前面的替换列表(用于带
的行和带-End-
的列表),以便于sed处理(posix sed中很难使用\n类内字符)
- 将所有行放入缓冲区(为替换列表和-End-before添加行分隔符)
- 如果这是
-End-³
,请删除该行并转到最终打印
- 用第二个模式(第2组)替换文本中的第一个模式(第1组)
- 如果找到,重新启动(
t再次
)
- 删除第一行
- 重新启动进程(
t再次
)。需要t,因为b
不会重置测试,并且下一个t
始终为真
多亏了上面的@miku
我有一个100MB的文件,其中包含80k替换字符串的列表
我尝试了sed的顺序或并行的各种组合,但没有发现吞吐量比大约20小时的运行时间短
相反,我将我的列表放入了一系列脚本中,比如“cat-in | replace-aoldanew-bold bnew-cold-cnew…>out;rm-in;mv-out-in”
我在每个文件中随机挑选了1000个替换项,所以都是这样的:
$ sed -e 's/A/B/g; ...' file.txt | \
sed -e 's/B/C/g; ...' | \
sed -e 's/C/D/g; ...' | \
sed -e 's/D/E/g; ...' > out
# first, split my replace-list into manageable chunks (89 files in this case)
split -a 4 -l 1000 80kReplacePairs rep_
# next, make a 'replace' script out of each chunk
for F in rep_* ; do \
echo "create and make executable a scriptfile" ; \
echo '#!/bin/sh' > run_$F.sh ; chmod +x run_$F.sh ; \
echo "for each chunk-file line, strip line-ends," ; \
echo "then with sed, turn '{long list}' into 'cat in | {long list}' > out" ; \
cat $F | tr '\n' ' ' | sed 's/^/cat in | replace /;s/$/ > out/' >> run_$F.sh ;
echo "and append commands to switch in and out files, for next script" ; \
echo -e " && \\\\ \nrm in && mv out in\n" >> run_$F.sh ; \
done
# put all the replace-scripts in sequence into a main script
ls ./run_rep_aa* > allrun.sh
# make it executable
chmod +x allrun.sh
# run it
nohup ./allrun.sh &
…运行时间不到5分钟,远远少于20小时
回过头来看,我本可以在每个脚本中使用更多的对,通过找出多少行可以弥补这个限制
xargs --show-limits </dev/null 2>&1 | grep --color=always "actually use:"
Maximum length of command we could actually use: 2090490
因此,我似乎可以在这个主题上使用2*40000行块,使用N个-e
或N个单数sed命令时sed运行得更快吗?当N>100.IIRC时,在单个sed
命令中使用替换的N
比N
numbersed
命令快一点。我记得being有点惊讶,并行运行几百个进程并没有降低性能
head -c 2090490 80kReplacePairs | wc -l
76923