在使用这个网站的这么多年里,我从来不需要问一个问题,因为总是有一个答案(通常是很多)。我很确定这个也能,但我一生都找不到它。
我有一个包含一堆文件的目录,其中有许多随机长度的行。
a.txt
b.txt
c.txt
d.txt
然后我有一个eg.txt
带有一组字符串列表的文件
opq 111
rst 222
uvw 333
xyz 444
每个 txt 文件都有一个我想要替换的字符串
a.txt has a#P#b
b.txt has c#P#d
c.txt has e#P#f
d.txt has g#P#h
我想替换#P#
为字符串文件中的第二个“列”。每个文件只发生#P#
一次(因为我把它放在那里)。结果将是
a.txt has a111b
b.txt has c222d
c.txt has e333f
d.txt has g444h
“不变”的假设是我的目录中有多少行eg.txt
就有多少.txt
个文件,并且它们按字母顺序排列。中的行eg.txt
按照“列”1 按字母顺序排序
我一直在尝试在 for 循环中使用awk
and sed
(实际上是sd
)来做到这一点,但我无法让它逐行读取“源”和“目标”。
我对如何实现结果并不挑剔。目前我没有处理很多行或文件(现在有 15 行和 15 个文件),但有时会有很多行或文件。我在基于 Arch 和 Debian 的 Linux 发行版(有时是 WSL 2)上使用 zsh 作为 shell
如果有答案,我们深表歉意。在过去的两天里,我在做这个项目时真的很努力地试图找到它,但我的大脑现在已经耗尽了。
编辑:更新以澄清目录中的文件有许多不同长度的行,并且我给定的字符串#P#
每个文件仅出现一次
答案1
使用 GNU awk 进行“就地”编辑ARGIND
:
awk -i inplace '
NR == FNR { map[NR]=$2 }
NR != FNR { sub(/#P#/,map[ARGIND]) }
1' eg.txt ?.txt
上面假设替换文本eg.txt
不包含空格或&
s。
答案2
准备工作
每个文件中只有一行。
$ grep -- . ?.txt
a.txt:a#P#b
b.txt:c#P#d
c.txt:e#P#f
d.txt:g#P#h
$ cat input
opq 111
rst 222
uvw 333
xyz 444
解决方案
sed
对每个文件进行 shell 循环调用:
for file in ?.txt; do
read -r dummy new_string rest
sed -- "s/#P#/$new_string/g" "$file"
done <input
a111b
c222d
e333f
g444h
如果您对文件更改的结果感到满意,请将其更改sed -i
为 GNUsed
或兼容版或sed -i ''
FreeBSD或兼容版。sed
上面假设行input
不包含&
, /
, 或\
字符。如果可能的话,您必须首先转义那些带有反斜杠的内容。
答案3
#!/bin/sh
mv eg.txt eg.input
awk 'NR==FNR{a[++i]=$2;next}{sub("#P#",a[++j]);print>(FILENAME".new")}' eg.input ./*.txt &&
for f in *.txt; do mv "$f.new" "$f"; done
mv eg.input eg.txt
eg.txt
重命名为eg.input
,然后返回,以便*.txt
awk 行仅扩展到应修改的文件。
NR==FNR{ #For the first file, eg.input
a[++i]=$2 #Put the second field in the array `a`
next #Skip the rest of the code
}
{ #For the other files
sub("#P#",a[++j]) #Make the substitution
print>(FILENAME".new") #Print to the line to `FILENAME`.new
}
然后,在 for 循环中,旧*.txt
文件内容将被文件内容覆盖*.new
。您可能想要抑制 for 循环,直到确信*.new
文件正确为止。
某些 awk 实现不能处理许多打开的文件(GNU awk 可以)。如果您的 awk 退出时出现“打开文件过多”错误,请使用此变体,
awk 'NR==FNR{a[++i]=$2;next}FNR==1{close(fn);fn=FILENAME".new"}{sub("#P#",a[++j]);print>fn}'
答案4
eg.txt
opq 111
rst 222
uvw 333
xyz 444
a.txt
a#P#b
12345
apple
b.txt
c#P#d
56788
命令
j=1;for i in "a.txt" "b.txt" ; do b=`sed -n ''$j'p' eg.txt| awk '{print $2}'`;sed "s/#P#/$b/g" $i;echo "=================";j=$(($j+1)); done
output
below are the output of a.txt
a111b
12345
apple
=================
below are the output of b.txt
c222d
56788
=================