在Bash中从文件中删除重复出现的行的最佳方法是什么?

时间:2010-06-21 19:46:06

标签: bash

民间,

我有一个包含ldap条目的文件,我想从第二次出现并删除“version:1”行。我知道sed可以做这样的事情,但由于我很新,我不知道如何继续。 这是Solaris 10计算机,文件如下所示:

version: 1
dn: uid=tuser1,ou=people,o=example.com,o=isp
cn: tuser1
uidNumber: 3
gidNumber: 3
homeDirectory: /export/home/tuser1
loginShell: /bin/sh
objectClass: posixAccount
objectClass: shadowAccount
objectClass: account
objectClass: top
uid: tuser1
shadowLastChange:
userPassword:

version: 1
dn: uid=tuser2,ou=people,o=example.com,o=isp
uidNumber: 20
cn: tuser1
gidNumber: 3
homeDirectory: /export/home/tuser2
loginShell: /bin/sh
objectClass: posixAccount
objectClass: shadowAccount
objectClass: account
objectClass: top
uid: tuser1
shadowLastChange:
userPassword: 

version: 1
dn: uid=tuser3,ou=people,o=example.com,o=isp
uidNumber: 10
cn: tuser3
gidNumber: 3
homeDirectory: /export/home/tuser3
loginShell: /bin/sh
objectClass: posixAccount
objectClass: shadowAccount
objectClass: account
objectClass: top
uid: tuser3
shadowLastChange:
userPassword: 

version: 1
dn: uid=loperp,ou=people,o=example.com,o=isp
uid: loperp
userPassword:
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
sn: pop
cn: loper

version: 1
dn: uid=tuser4,ou=people,o=example.com,o=isp
userPassword: 
uid: tuser4
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
sn: User4
cn: Test

4 个答案:

答案 0 :(得分:5)

GNU sed

sed -ni '0,/version: 1/{p; d}; /version: 1/!p' ldap.txt
编辑:这最初是错误的。当第一行不是版本时,它会打印副本。

GNU版本更简单。它从头开始打印(p),直到匹配版本正则表达式的第一行(包括两者)。此外,对于该范围内的每一行,打印后我们删除模式空间并开始一个新的循环(d)。基本上,这意味着转到脚本的开头和下一行(这避免了双重打印)。与(标准)1,/regex/不同,如果第一行匹配,则不会继续另一个匹配行。

如果我们没有d'ed(因此我们在第一个version: 1之后),那么我们只需打印与正则表达式不匹配的每一行(!

使用标准sed):

sed -ni 'p; /version: 1/ b nov; d; :nov /version: 1/!p; n; b nov' ldap.txt

首先打印每一行(p)。在那次打印之后,如果我们匹配正则表达式,我们分支到nov(无版本)标签;标签名称取决于我们。如果我们不分支,我们(d)删除模式空间并开始一个新的循环(换行符,脚本开头)。在nov中,如果行不匹配(与GNU相同),则打印该行。然后我们去一个新的线路,然后分支回到nov。这个循环一直持续到结束。


我(Jonathan Leffler)可以确认@ kuti对Solaris 10标准'sed'的观察;有效的是:

/bin/sed -n 'p
/version: 1/ b nov
d
:nov
/version: 1/!p
n
b nov' ldap.txt

代替换行符的'冒号'似乎并不普遍适用于Solaris'sed'。具体而言,至少在使用标签后不能出现分号。

这似乎有效:

/bin/sed -n 'p; /version: 1/ b nov
d; :nov
/version: 1/!p; n; b nov' ldap.txt

我想不出如何在评论中提供修复 - 多行格式在这里至关重要。

答案 1 :(得分:2)

一个简单的答案使用awk:

awk '{ if ($0 ~ /^version: 1$/) { if (count++ == 0) print; }
       else print;
     }'

这假设您的确意味着您只想要第一个'version:1'行,并且不介意保留多个'version:2'行等。

答案 2 :(得分:0)

这是另一个awk版本

awk '/version: 1/{c++}c>1{gsub("version: 1","")}1' file

答案 3 :(得分:0)

使用man 1 ed我们可以标记包含第一个匹配的行并将其递增1以获得:

#  'm+1,$  
#  ... which creates a line address space of:  
#  /first line matched + 1/,/last line/

# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
[[ $(grep -c -m 1 '^version: 1' file) -eq 1 ]] && \
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s file
   H
  /^version: 1/km
  'm+1,$g/^version: 1/d
  wq
EOF