有时,表格数据中会有一些非常烦人的行,例如
column name | other column name
-------------------------------
我通常更喜欢通过合理唯一的字符串来删除不应该存在的垃圾行grep -v
,但这种方法的问题是,如果合理唯一的字符串意外出现在数据中,那就是一个严重的问题。
有没有办法限制可以删除的行数grep -v
(比如 1)?对于奖励积分,有没有一种方法可以在不使用 的情况下计算从末尾开始的行数<some command> | tac | grep -v <some stuff> | tac
?
答案1
您可以awk
忽略第一个n匹配的行(例如,假设您只想从文件中删除第一个和第二个匹配项):
n=2
awk -v c=$n '/PATTERN/ && i++ < c {next};1' infile
忽略最后一个n匹配的行:
awk -v c=${lasttoprint} '!(/PATTERN/ && NR > c)' infile
其中是文件中最后一个匹配${lasttoprint}
的第 th+1 行号。n
有多种方法可以获取该行号。 (例如,通过sed
/等工具仅打印每个匹配的行号awk
,然后tail | head
提取它)...这是一种方法gnu awk
:
n=2
lasttoprint=$(gawk -v c=$((n+1)) '/PATTERN/{x[NR]};
END{asorti(x,z,"@ind_num_desc");{print z[c]}}' infile)
答案2
sed
提供了一个更简单的方法:
... | sed '/some stuff/ {N; s/^.*\n//; :p; N; $q; bp}' | ...
这样您就可以删除第一个出现的位置。
如果你想要更多:
sed '1 {h; s/.*/iiii/; x}; /some stuff/ {x; s/^i//; x; td; b; :d; d}'
,其中 count ofi
是出现次数(一次或多次,而不是零)。
多行解释
sed '1 {
# Save first line in hold buffer, put `i`s to main buffer, swap buffers
h
s/^.*$/iiii/
x
}
# For regexp what we finding
/some stuff/ {
# Remove one `i` from hold buffer
x
s/i//
x
# If successful, there was `i`. Jump to `:d`, delete line
td
# If not, process next line (print others).
b
:d
d
}'
此外
也许,这个变体会工作得更快,因为它会读取所有其余行并一次性打印它们
sed '1 {h; s/.*/ii/; x}; /a/ {x; s/i//; x; td; :print_all; N; $q; bprint_all; :d; d}'
结果
您可以将此代码放入您的外壳.bashrc
(或外壳的配置中,如果是其他外壳):
dtrash() {
if [ $# -eq 0 ]
then
cat
elif [ $# -eq 1 ]
then
sed "/$1/ {N; s/^.*\n//; :p; N; \$q; bp}"
else
count=""
for i in $(seq $1)
do
count="${count}i"
done
sed "1 {h; s/.*/$count/; x}; /$2/ {x; s/i//; x; td; :print_all; N; \$q; bprint_all; :d; d}"
fi
}
并以这种方式使用它:
# Remove first occurrence
cat file | dtrash 'stuff'
# Remove four occurrences
cat file | dtrash 4 'stuff'
# Don't modify
cat file | dtrash
答案3
也许可以通过使用更准确的 grep 命令来减少过滤数据的机会。例如:
grep -v -F -x 'str1'
对于以下线路完全地str1。或者可能:
grep -v '^str1.*str2$'
对于以“str1”开头并以“str2”结尾的行。
答案4
另一种可能的解决方案是使用 bash 自己的实用程序:
count=1
found=0
cat execute-commons-fileupload.sh | while read line
do
if [[ $line == *"myPattern"* ]]
then
if [ $found -eq $count ]
then
echo "$line"
else
found=$(($found+1))
fi
else
echo "$line"
fi
done
通过设置计数,您可以更改要删除的模式的出现次数。
对我个人来说,这似乎更容易扩展,因为您可以轻松地在语句中添加其他条件if
(但这可能是由于我对 sed 的了解有限所致)。