我有一个文件夹,里面有几个子文件夹和文件。还有一个 CSV 文件,其中包含子文件夹的名称以及子文件夹内文件中的模式。
我想要做的是使用主文件夹内的 while 循环读取 CSV 文件,并使用 删除匹配的模式文件sed
。我bash
在 Unix 中使用这个shell 脚本:
IFS=","
while read f1 f2
do
find $f2/ -name vcs*.pv -exec sed -i '/$f1/d' {} +
done > export.csv
错误:
find: 'ap01\r/': No such file or directory.
CSV 文件:
S2AEC67X1,ap01
它正在f2
正确读取值,但不执行其余操作。我将 CSV 文件保存在包含所有子文件夹的主目录中。
答案1
这:ap01\r
表明有一个回车在字符串之后api01
。尝试从 CSV 文件中删除它。
更新:另请阅读@RomanPerekhrest 的评论。您需要将循环中的 更改>
为 a (如果您确实应该这样做)<
while
使用一个while
循环,但这完全是另一个讨论!)。
答案2
这个问题中的代码和输入文件存在一些问题:
输入文件的
\r
每一行显然都有尾随回车符 ( )。这可能是由于在 Windows 计算机上将 hit 创建为 DOS 文本文件。摆脱这些回车符的通常方法是dos2unix
在文件上运行。例如,请参阅问题什么是“^M”以及如何摆脱它?所有变量扩展都应该用双引号引起来。在您的命令中,您使用
$f2
不带引号的作为目录的路径名。如果$f2
包含空格,这将失败。单引号阻止 shell 扩展变量,这意味着您的
sed
脚本正在查找与文字正则表达式匹配的行$f1
。这个正则表达式永远不会匹配,因为$
只会匹配行尾,并且不会有行结束然后包含f1
同一行上的字符。双引号sed
编辑脚本将使 shell$f1
在调用之前展开变量sed
。该模式
vcs*.pv
应该是-name
选项的参数find
,但由于它未加引号,因此它将扩展到当前目录中与该通配模式匹配的任何名称。因此,如果当前目录中有一个名为 的文件vcs-test.pv
,find
将被调用-name vcs-test.pv
,并且您只能找到具有该名称的文件。如果当前目录中有多个匹配的名称,您可能会find
抱怨未知选项。文件
export.csv
被输出到(并在循环发生任何输出之前清空)。您可能希望循环从中读取内容。这涉及更改>
为<
.
脚本已更正:
while IFS=',' read f1 f2; do
find "$f2" -type f -name 'vcs*.pv' -exec sed -i "/$f1/d" {} +
done <export.csv
我还添加了-type f
到find
命令行,因为我们可能不想意外获取目录名称。我也这样做了,以便IFS
设置变量仅有的为read
命令。
这是上述情况的变体,在所有文件都已定位的情况下直接地在您从 CSV 文件中读取其名称的顶级目录下方:
while IFS=',' read dir pattern; do
for name in "$dir"/vcs*.pv; do
test -f "$name" && sed -i "s/$pattern/d" "$name"
done
done <export.csv
这样做的好处是你可以摆脱find
。 坏处是现在sed
每个文件都需要调用一次 (通常这不是问题,除非你有数百个或更多的文件)。
以下是上面的变体,根据以下内容删除行细绳从 CSV 文件中读取。不同之处在于,上面的代码片段将模式解释为正则表达式,不作为固定字符串。如果您的字符串包含在正则表达式中被解释为“特殊”的字符(例如.
、*
、等),[
则这一点很重要。]
while IFS=',' read dir string; do
for name in "$dir"/vcs*.pv; do
[ ! -f "$name" ] && continue
grep -v -F -e "$string" "$name" >"$name.tmp" && mv -f "$name.tmp" "$name"
done
done <export.csv