我不是Linux专家,通常在这种情况下PHP会更合适......但是由于我在Bash中编写它的情况发生了:)
我有以下.sh,它运行在当前文件夹中的所有.csv文件中并执行一堆命令。 目标:清理.csv文件中的电子邮件列表(实际上不是.csv,但实际上只是.txt文件)。
for file in $(find . -name "*.csv" ); do
echo "====================================================" >> db_purge_log.txt
echo "$file" >> db_purge_log.txt
echo "----------------------------------------------------" >> db_purge_log.txt
echo "Contacts BEFORE purge:" >> db_purge_log.txt
wc -l $file | cut -d " " -f1 >> db_purge_log.txt
echo " " >> db_purge_log.txt
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
mv tmp_file $file ;
echo "Contacts AFTER purge:" >> db_purge_log.txt
wc -l $file | cut -d " " -f1 >> db_purge_log.txt
done
现在问题是:
我想在此循环中间的某处添加一个命令,使用另一个.csv文件作为抑制列表,这意味着 - 在该抑制列表中找到完美匹配的每一行 - 从$file
删除。 / p>
此时我的大脑被卡住了,我想不出解决方案。说实话,我没有管理在2个不同的文件上使用sort
或grep
并导出到第3个文件而没有完全消除跨两个文件的重复行,所以我最终得到的数据少得多。
非常感谢任何帮助!
答案 0 :(得分:4)
在向脚本添加功能之前,需要清理现有脚本 - 很多。
当我看到像这样的墙到墙I / O重定向时,我想哭 - 这不是你怎么做的!你有三个选择来避免这一切:
for file in $(find . -name "*.csv" )
do
echo "===================================================="
echo "$file"
echo "----------------------------------------------------"
echo "Contacts BEFORE purge:"
wc -l $file | cut -d " " -f1
echo " "
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
mv tmp_file $file ;
echo "Contacts AFTER purge:"
wc -l $file | cut -d " " -f1
done >> db_purge_log.txt
或者:
{
for file in $(find . -name "*.csv" )
do
echo "===================================================="
echo "$file"
echo "----------------------------------------------------"
echo "Contacts BEFORE purge:"
wc -l $file | cut -d " " -f1
echo " "
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
mv tmp_file $file ;
echo "Contacts AFTER purge:"
wc -l $file | cut -d " " -f1
done
} >> db_purge_log.txt
甚至:
exec >>db_purge_log.txt # By default, standard output will go to db_purge_log.txt
for file in $(find . -name "*.csv" )
do
echo "===================================================="
echo "$file"
echo "----------------------------------------------------"
echo "Contacts BEFORE purge:"
wc -l $file | cut -d " " -f1
echo " "
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
mv tmp_file $file ;
echo "Contacts AFTER purge:"
wc -l $file | cut -d " " -f1
done
第一个表单适用于此脚本,其中包含一个循环以提供I / O重定向。使用{
和}
的第二种形式将处理更一般的命令序列。使用exec
的第三种形式是“永久的”;您无法恢复原始标准输出,而使用{
... }
表单,您可以将脚本的不同部分写入不同的位置。
所有这些变化的另一个优点是,如果您想要的话,您可以轻松地将错误发送到您发送标准输出的相同位置。例如:
exec >>db_purge_log.txt 2>&1
从wc
中取消文件名 - 而不是:
wc -l $file | cut -d " " -f1
使用:
wc -l < $file
UUOC - 无用地使用cat
- 代替:
cat $file | egrep -v "xxx|yyy|zzz" | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
使用:
egrep -v "xxx|yyy|zzz" $file | grep -v -E -i '([0-z])\1{2,}' | uniq | sort -u > tmp_file
UUOU - 无用地使用uniq
您完全不清楚为什么需要uniq
和sort -u
;在上下文中,sort -u
就足够了,所以:
egrep -v "xxx|yyy|zzz" $file | grep -v -E -i '([0-z])\1{2,}' | sort -u > tmp_file
UUOG - 无用地使用grep
egrep
相当于grep -E
,两者都能够处理多个正则表达式,第二个将匹配括号中表达式匹配的3次或更多次(我们实际上只需要匹配三次),所以事实上第二个表达式将完成第一个的工作。并且[0-z]
匹配是可疑的。它可能匹配各种标点符号以及大写和小写数字,但由于-i
,您已经在进行不区分大小写的搜索,因此我们可以将所有这些都规范化为:
grep -Eiv '([0-9a-z]){3}' $file | sort -u > tmp_file
带空格的文件名
由于for file in $(find ...)
表示法,代码不会使用空格,制表符或换行符处理文件名。现在可能没有必要处理 - 请注意这个问题。
for file in $(find . -name "*.csv" )
do
echo "===================================================="
echo "$file"
echo "----------------------------------------------------"
echo "Contacts BEFORE purge:"
wc -l < $file
echo " "
grep -Evi '([0-9a-z]){3}' | sort -u > tmp_file
mv tmp_file $file
echo "Contacts AFTER purge:"
wc -l <$file
done >> db_purge_log.txt
我想在此循环中间的某处添加一个命令,以使用另一个
.csv
文件作为抑制列表 - 这意味着应该从{{1}删除在该抑制列表中找到的完美匹配的每一行}}
由于我们已经对输入文件($file
)进行了排序,因此我们可以对抑制文件进行排序(如果尚未对其进行排序,则将其称为$file
。鉴于此,我们将使用{{ 1}}消除suppfile='suppressions.txt'
和comm
中出现的行。我们对仅出现在$file
中的行感兴趣(或者,就像这里的情况一样,已编辑和排序的文件版本),因此我们要禁止$suppfile
中未出现的$file
中的公共条目和条目。$suppfile
命令读取已编辑的,已排序的来自标准输入$file
的文件,并从comm -23 - "$suppfile"
-
如果抑制文件未按排序顺序排列,只需将其排序为临时文件即可。注意在当前目录中的抑制文件上使用"$suppfile"
后缀;它将捕获文件并清空它,因为抑制文件中的每一行都与抑制文件中的一行匹配,这对抑制文件后处理的任何文件都没有帮助。
哎呀 - 我过度简化了suppfile='suppressions.txt' # Must be in sorted order
for file in $(find . -name "*.csv" )
do
echo "===================================================="
echo "$file"
echo "----------------------------------------------------"
echo "Contacts BEFORE purge:"
wc -l < "$file"
echo " "
grep -Evi '([0-9a-z]){3}' | sort -u | comm -23 - "$suppfile" > tmp_file
mv tmp_file "$file"
echo "Contacts AFTER purge:"
wc -l < "$file"
done >> db_purge_log.txt
正则表达式。它应该(可能)是:
.csv
差异很大。我的原始重写将查找任意三个相邻的数字或字母(例如grep
或grep -Evi '([0-9a-z])\1{2}' $file
);修订版(实际上非常类似于原始命令之一)查找来自123
的字符,后跟两次出现的相同字符(例如abz
或[0-9A-Za-z]
,但不是{{1 }或111
)。
如果偶然的替代aaa
实际上不是3个重复的字符,则可能需要按顺序进行两次123
的调用。
答案 1 :(得分:1)
如果我理解正确,假设最近有一个&#39; nix,grep
应该为你做大部分工作。命令grep -vf filterfile input.csv
将输出input.csv中与filterfile中找不到的任何正则表达式不匹配的行。
其他几条评论...... uniq
需要对输入进行排序才能删除重复项,因此您可能需要管道中的sort
(除非输入数据已排序)。
或者,如果输入按开头排序,grep -u
将忽略重复项。
小建议 - 您可以添加#!/bin/bash
作为第一行,以确保脚本由bash而不是用户的登录shell运行(可能不是bash)。
HTH。
B'/ P>