bash sed在while循环中失败

meA*_*low 0 bash sed

#!/bin/bash
fname=$2
rname=$1
echo "$(<$fname)" | while read line ; do
    result=`echo "$(<$rname)" | grep "$line"; echo $?`
    if [ $result != 0 ]
    then
        sed  '/$line/d' $fname > newkas
    fi 2> /dev/null
done
Run Code Online (Sandbox Code Playgroud)

大家好,我是bash的新手.

我有两个比另一个更旧的列表.我希望将'fname'上的名字与'rname'进行比较.'结果'是标准输出,如果名称仍然在'rname'中可用,我将获得.如果不是那么我将获得非零输出.使用sed删除该行并将其重新路由到新文件.

我已经尝试了部分代码,直到我添加了while循环函数.sed似乎不起作用,因为'newkas'的最终输出与初始输入'fname'相同.我的方法错了还是错过了任何部分?

Sor*_*gal 6

第1部分:出了什么问题

你的sed表达"不起作用"的原因是因为你使用了单引号.你说

sed  '/$line/d' $fname > newkas
Run Code Online (Sandbox Code Playgroud)

假设fname=input.txt',line='example text'这将扩展到:

sed  '/$line/d' input.txt > newkas
Run Code Online (Sandbox Code Playgroud)

请注意,$line仍然存在.这是因为bash不会插单引号内的变量,从而sed看到$字面上.

你可以解决这个问题

sed  "/$line/d/" $fname > newkas
Run Code Online (Sandbox Code Playgroud)

因为在双引号内,变量将扩展.但是,如果你的sed表达式变得更复杂,那么在bash解释你想要解释的东西的情况下,你可能会遇到困难sed.我倾向于使用表格

sed '/'"$line"'/d/' $fname > newkas
Run Code Online (Sandbox Code Playgroud)

这有点难以阅读但是,如果仔细观察,单引号我打算成为sed表达式的一部分,并双引号我要扩展的变量.

第2部分:如何改进它

您的脚本包含一些可以改进的内容.

echo "$(<$fname)" | while read line ; do
    :
done
Run Code Online (Sandbox Code Playgroud)

首先,"$(<$fname)"当你可以重定向while循环的stdin时,你正在读取文件.这有点多余,但更重要的是你要管道while,这会创建一个额外的子shell,这意味着你不能修改封闭范围内的任何变量.更好的说法

while IFS= read -r line ; do
    :
done < "$fname"
Run Code Online (Sandbox Code Playgroud)

接下来,考虑你的 grep

echo "$(<$rname)" | grep "$line"
Run Code Online (Sandbox Code Playgroud)

你再次阅读文件并将其回显给grep.但是,grep可以直接读取文件.

grep "$line" "$rname"
Run Code Online (Sandbox Code Playgroud)

然后,您回显返回代码并在if语句中检查其值,这是一个经典的无用构造.

result=$( grep "$line" "$rname" ; echo $?)
Run Code Online (Sandbox Code Playgroud)

相反,您可以直接传递grepif,它将测试其返回代码.

if grep -q "$line" "$rname" ; then
    sed  "/$line/d" "$fname" > newkas
fi
Run Code Online (Sandbox Code Playgroud)

请注意,我引用了$fname,如果它可能包含空格,这很重要.我还添加-qgrep,它抑制了它的输出.

现在,无需if在此处声明来自语句的错误消息,因为我们不必担心$result包含异常值或grep无法正确返回.

最终结果是这个脚本

while IFS= read -r line ; do
    if grep -q "$line" "$rname" ; then
        sed  "/$line/d" "$fname" > newkas
    fi
done < "$fname"
Run Code Online (Sandbox Code Playgroud)

哪个不起作用,因为newkas在每个循环中都会被覆盖.这意味着最后只使用了最后一行$fname.相反,你可以说:

cp "$fname" newkas
while IFS= read -r line ; do
    if grep -q "$line" "$rname" ; then
        sed  -i '' "/$line/d" newkas
    fi
done < "$fname"
Run Code Online (Sandbox Code Playgroud)

我相信,这会做你期望的.

第3部分:但不要这样做

但这与解决您的实际问题完全相关.在我看来,你只想创建一个文件newkas,其中包含$fname除了出现的那些行之外的所有行$rname.这可以通过comm实用程序轻松完成:

comm -2 -3 <(sort "$fname") <(sort "$rname") > newkas
Run Code Online (Sandbox Code Playgroud)

这也会改变行的排序顺序,这可能对你不利.如果你想在不改变排序的情况下这样做,那么使用@fge建议的方法是最好的.

grep -F -v -x -f "$rname" "$fname"
Run Code Online (Sandbox Code Playgroud)