如何在Linux中使用sed命令添加字符并替换它

3

我有一个需求。

我有一个名为a.txt的文本文件,其中包含单词列表 -

GOOGLE
FACEBBOK

现在我还有另一个名为b.txt的文件,其中包含以下内容

Company name is google.
Company name is facebook.

有 n 行不同的单词,就像这样。

接下来我将编写脚本文件 -

    FILENAME="a.txt"

SCHEMA=$(cat $FILENAME)

for L in $SCHEMA
do
    echo "${L,,}"

sed -i -E "s/.+/\L&_/" b.txt
done

运行脚本后,我期望得到的是 b.txt 文件的输出文件

 Company name is google_
 Company name is facebook_

但是运行该脚本后,我得到的输出是 -
Company name is google.__
Company name is facebook.__

如我在 sed 命令中提到的那样,这个输出将被保存在 b.txt 文件中。

注意 - 在 a.txt 中,我有要替换的单词列表,在 b.txt 文件中,我有一些段落,其中包含诸如google。facebook。等单词。

这就是为什么我无法直接使用 sed 命令进行替换的原因。

希望你能理解我的需求。

先行致谢!


1
顺带一提,不要为你的私有变量使用大写字母。 - tripleee
4个回答

1
您可以使用以下GNU sed解决方案:
FILENAME="a.txt"
while IFS= read -r L; do
  sed -i "s/\($L\)\./\1_/gI" b.txt
done < $FILENAME

或者,同样地,在没有循环的情况下作为单行代码(在 anubhava's answer 中使用):

sed -i -f <(printf 's/\\(%s\\)\\./\\1_/gI\n' $(<"$FILENAME")) b.txt

使用该脚本,您可以:

  • while IFS= read -r L; do - 逐行读取文件,并将每行分配给变量L
  • sed -i "s/\($L\)\./\1_/gI" b.txt - 替换b.txt文件中所有不区分大小写的以L结尾的句号.,替换为相同值的L并附加下划线_
  • -f允许将一系列命令传递给sed
  • printf 's/\\(%s\\)\\./\\1_/gI\n' $(<"$FILENAME") 创建了一个包含sed命令列表的字符串,例如:
s/\(GOOGLE\)\./\1_/gI
s/\(FACEBOOK\)\./\1_/gI

感谢@Wiktor Stribiżew的回复。实际上,在应用您的脚本后,我得到的输出是- google.__和facebook.,它添加了双下划线"",并且没有消除"."。你能帮我解决这个问题吗? 实际上,在a.txt文件中,我有一个要更改为b.txt文件中的单词列表。因此,对于每个单词应用循环。 - saurabh704
@saurabh704 如果输入中没有 .,为什么要消除 .?还是您分享了错误的输入?您是否使用了 sed -i -E "s/.+/\L&_/" 或者 sed -i "s/.*/\L&_/"?如果您使用了后者,请尝试 sed -i "s/..*/\L&_/" - Wiktor Stribiżew
感谢@Wiktor Stribiżew抽出时间来帮忙。我已经编辑了问题,请看看您是否能够理解我的要求。 - saurabh704
@saurabh704 看看我的新回答吧。是的,在编辑后,问题更清晰了。 - Wiktor Stribiżew

1

以下是如何使用单个shell命令在没有循环的情况下使用gnu-sedprintf进行处理:

sed -i -E -f <(printf 's/\\b(%s)\\./\\1_/I\n' $(<a.txt)) b.txt

cat b.txt

Company name is google_
Company name is facebook_

这比在循环中运行sedawk更有效,特别是当输入文件很大时。
  • printf命令正在创建一个类似于以下的sed命令脚本:
s/\b(GOOGLE)\./\1_/I
s/\b(FACEBOOK)\./\1_/I
  • sed -f 运行动态生成的脚本。

1
非常感谢@anubhava。即使不使用循环,它也对我有用。 - saurabh704
1
@saurabh704,嘿Saurabh,你能否也检查一下我的解决方案是否对你有帮助? - RavinderSingh13

1

请使用单个awk读取2个输入文件,尝试以下操作。

awk '
FNR==NR{
  a[tolower($0)]
  next
}
($(NF-1) in a){
  sub(/\.$/,"")
  print $0"_"
}
' a.txt FS="[ .]" b.txt

解释:为上述解决方案添加详细的说明。
awk '                        ##Starting awk program from here.
FNR==NR{                     ##Checking condition FNR==NR which will be TRUE when a.txt is being read.
  a[tolower($0)]             ##Creating array a with index of current line in lower case from a.txt here.
  next                       ##next will skip all further statements from here.
}
($(NF-1) in a){              ##Checking condition if 2nd last field is present in array a then do following.
  sub(/\.$/,"")              ##Substituting last DOT with NULL here.
  print $0"_"                ##Printing current line with _ here.
}
' a.txt FS="[ .]" b.txt      ##Mentioning a.txt and setting field separator as space and . for b.txt here.


第二种解决方案:在这里添加一个使用awk的解决方案。

awk '
FNR==NR{
  a[tolower($0)]
  next
}
{
  sub(/\.$/,"")
}
($NF in a){
  print $0"_"
}
' a.txt b.txt

0
这可能适用于您(GNU sed):
sed 's#.*#s/(&)./\\1_/Ig#' a.txt | sed -i -Ef - b.txt

注意:由于替换命令上的I标志,匹配不区分大小写,但替换是从原始文件中进行的,即如果原始字符串为google,则匹配不区分大小写到GOOGLE并替换为google_


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接