bash sed在while循环中失败

时间:2011-12-28 10:20:34

标签: 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

大家好,我是bash的新手。

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

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

2 个答案:

答案 0 :(得分:5)

第1部分:出了什么问题

sed表达式“不起作用”的原因是因为您使用了单引号。你说

sed  '/$line/d' $fname > newkas

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

sed  '/$line/d' input.txt > newkas

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

你可以通过说

来解决这个问题
sed  "/$line/d/" $fname > newkas

因为在双引号内,变量将会扩展。但是,如果您的sed表达式变得更复杂,那么在bash解释您打算由sed解释的内容时,可能会遇到困难。我倾向于使用表格

sed '/'"$line"'/d/' $fname > newkas

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

第2部分:如何改进

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

echo "$(<$fname)" | while read line ; do
    :
done

首先,当您可以重定向"$(<$fname)"循环的标准输入时,您正在使用while读取文件。这有点多余,但更重要的是,您正在使用while,它会创建一个额外的子shell,这意味着您无法修改封闭范围内的任何变量。更好的说法

while IFS= read -r line ; do
    :
done < "$fname"

接下来,请考虑您的grep

echo "$(<$rname)" | grep "$line"

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

grep "$line" "$rname"

之后,您将回显返回代码,并在if语句中检查其值,该语句为classic useless construct

result=$( grep "$line" "$rname" ; echo $?)

相反,您可以直接将grep传递给ifif grep -q "$line" "$rname" ; then sed "/$line/d" "$fname" > newkas fi 将测试其返回码。

$fname

请注意,我引用了-q,如果它可能包含空格,这很重要。我还将grep添加到if,这会抑制其输出。

此处无需取消$result语句中的错误消息,因为我们不必担心grep包含异常值或while IFS= read -r line ; do if grep -q "$line" "$rname" ; then sed "/$line/d" "$fname" > newkas fi done < "$fname" 无法正常返回。

最终结果是这个脚本

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" 中的最后一行。相反,你可以说:

newkas

我认为,这会做你期望的事。

第3部分:但不要那样做

但这与解决您的实际问题完全相关。在我看来,您只想创建一个文件$fname,其中包含$rname的所有行,但comm中显示的行除外。使用comm -2 -3 <(sort "$fname") <(sort "$rname") > newkas 实用程序

可以轻松完成此操作
grep -F -v -x -f "$rname" "$fname"

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

{{1}}

答案 1 :(得分:1)

如果我理解您的需要,您需要一个文件newaks,其中包含$fname中也在$rname中的行。

如果这是你想要的,使用sed是过度的。使用fgrep

fgrep -x -f $fname $rname > newkas

此外,您的脚本存在问题:

  • 您在grep中捕获result的输出,这意味着它永远不会完全为0;你想要的是执行命令,只需检查$?
  • 您的echo es是错综复杂的,只是grep whatever thefilenamewhile...done <thefile;
  • 最后,您从源文件中取出行:该行可能是正则表达式,这意味着您将尝试匹配$rname中的正则表达式,这可能会产生意外结果。

等等。