如何在使用 sed 命令时在模式中传递变量?

如何在使用 sed 命令时在模式中传递变量?

我有文件abc.sh

search_dir='dummy'
filename='numbers.txt'

for entry in "$search_dir"/*
do
  while read p;
  do 
    sed -i '' "/$p/d" $entry
  done < $filename
done

尝试删除具有匹配模式的行。基本上,模式只是我从文件传递的字符串。但不幸的是,它不起作用。

我能够调试的是,我没有以正确的模式传递变量。

编辑: 数字.txt

2018061300006178
2018061300006179
2018061300006325
2018061300006326
2018061400006505

search_dir 中存在的文件内容是:

1888~2018061400006505~0101~1~OWNED~SELF EMPLOYED~~~~3~~AGRICULTURE~~~OTHERS~AGRICULTURIST~~~AGRICULTURE~~~~~~~~N~N~Y~N~N~~300000-500000~~~49582E95361D5FA0C10C4C419B2940591C17E94EF329C31047A6B7DE26E68638
1889~2018061400006505~0101~2~OWNED~SELF EMPLOYED~~~~32~~AGRICULTURE~~~OTHERS~AGRIC

所以numbers.txt包含2018061400006505并且文件还包含数字相关数据,所以我想删除与给定数字匹配的行。

答案1

只要示例中的数字不包含sed正在使用的分隔符(默认情况下/),$p代码中的 the 将被解释为正则表达式(及其所有含义)。

你的代码:

search_dir='dummy'
filename='numbers.txt'

for entry in "$search_dir"/*
do
  while read p;
  do 
    sed -i '' "/$p/d" $entry
  done < $filename
done

在这里,您要删除下文件中$search_dir包含 中任何数字的所有行$filename。这是否有效取决于你的sed对待方式-i ''。对于某些实现,sed您必须-i在没有参数的情况下使用。

相关sed -i和可移植性:如何使用 sed -i (就地编辑)实现可移植性?

将结果写入临时文件然后将该文件移动到原始文件名会更安全:

for entry in "$search_dir"/*
do
  while read p;
  do 
    sed "/$p/d" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
  done <"$filename"
done

这可以确保无论sed您使用哪种实现,它都能正常工作。一般来说,在测试脚本时尝试对文件进行就地更改是一个坏主意,因此mv在您对脚本的其他工作方式感到满意之前,您可能需要注释掉这一点。

尽管作为通用解决方案,这仍然有点不安全,因为您实际上是“使用数据作为代码”(数字是数据,并且您将它们用作sed脚本的一部分)。这意味着您sed只需/在数字文件中的一个数字中插入 a ,就很容易在脚本中导致语法错误。

由于操作如此简单,我们可以改为使用grep.这也摆脱了内部while循环:

for entry in "$search_dir"/*
do
  grep -Fv -f "$filename" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
done

这将导致grep从文件中读取其模式$filename并将其应用到$entry文件。-v我们将丢弃任何包含模式的行,并且意味着-F我们grep不是将数字解释为正则表达式,但解释为固定字符串。-f "$filename"我们可以从grep中读取字符串$filename

如果下面可能有目录,$search_dir我们希望跳过这些:

for entry in "$search_dir"/*
do
  [ ! -f "$entry" ] && continue
  grep -Fv -f "$filename" "$entry" >"$entry.tmp" && mv "$entry.tmp" "$entry"
done

另一种更安全的操作方法是使用awk.由于上面的sedgrep解决方案,数字是匹配的任何地方就行而言,可以想象我们可能会删除错误的行。可以awk轻松匹配~数据中的第二个 - 分隔字段:

for entry in "$search_dir"/*; do
    [ ! -f "$entry" ] && continue
    awk -F '~' 'NR==FNR { num[$0]; next } !($2 in num)' "$filename" "$entry" >"$entry.tmp" &&
    mv "$entry.tmp" "$entry"
done

awk程序首先使用数字作为键填充关联数组/散列,然后打印$entry文件中第二个~- 分隔列不是该散列中的键的每一行。

相关内容