【问题标题】:How can I reformat the GECOS field with Perl or awk?如何使用 Perl 或 awk 重新格式化 GECOS 字段?
【发布时间】:2011-01-12 17:19:27
【问题描述】:

我想扫描passwd文件并将评论字段中的单词顺序从firstname lastname更改为lastname firstname,并将姓氏强制为大写。

所以,改变每一行:

jbloggs:x:9999:99:Joe Bloggs:/home/jbloggs:/bin/ksh

到:

jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh

我是 Perl 新手,我在 awk 中遇到了不同字段分隔符的问题。 感谢任何帮助。

【问题讨论】:

  • 您对 AWK 字段分隔符有什么问题?
  • 为什么下面的一些解决方案会输出原始顺序之外的记录?
  • @paul44 您是否使用逗号分隔的 GECOS 信息,或者您的密码文件中的所有条目是否都像您的示例一样具有“”?

标签: perl awk


【解决方案1】:

【讨论】:

    【解决方案2】:

    $ awk -v FS=":" '{split($5, a, " "); name = toupper(a[2]) " " a[1]; gsub($5, name); print $0}' passwd

    不过,如果你有中间名,那就不行了。

    编辑:更易于阅读的版本

    awk -v FS=":" '
    {
        split($5, name_parts, " ")
        name = toupper(name_parts[2]) " " name_parts[1]
        gsub($5, name)
        print $0
    }' passwd
    

    【讨论】:

    • $5 = name 工作得很好,不需要gsub。另外,我想你也想要一个-v OFS=:print 没有参数打印 $0,但我想这是一个品味问题。
    • $5 = name 没有-v OFS=: 将无法工作。如果您不包括两者,您将获得以空格分隔的字段。 gsub() 用结果替换了$0 的内容,所以在我的脚本中我不需要担心OFS
    • 哦,我明白你现在在做什么了。嗯,如果$5 = "." 怎么办?危险。
    • 对于空的 GECOS 字段、仅包含一个单词和逗号分隔的字段失败。
    • 谢谢大家 - 这太棒了。如果 GECOS 包含 $firstname $lastname $otherstuff - 例如 - bloggs:x:9999:99:Joe Bloggs temp (Sales):/home/jbloggs:/bin/ksh 那么我只想对全名进行操作,如上面,并丢弃其余的 GECOS?
    【解决方案3】:

    独立示例:

    use strict;
    use warnings;
    my $s = 'jbloggs:x:9999:99:Joe Bloggs:/home/jbloggs:/bin/ksh';
    my @tokens = split /:/, $s;
    my ($first, $last) = split /\s+/, $tokens[4];
    $tokens[4] = uc($last) . " $first";
    print join(':', @tokens), "\n";
    
    __END__
    jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh
    

    作为脚本(输出到 STDOUT;必须将输出重定向到文件):

    use strict;
    use warnings;
    while (<>) {
        chomp;
        my @tokens = split /:/;
        my ($first, $last) = split /\s+/, $tokens[4];
        $tokens[4] = uc($last) . " $first";
        print join(':', @tokens), "\n";
    }
    

    【讨论】:

    • 这对于逗号分隔的 GECOS 字段会失败,并且对于某些记录会给出“在连接 (.) 或字符串中使用未初始化的值 $first”错误。
    【解决方案4】:

    这将在您读取文件时对其进行处理,并将新格式条目放入数组@newEntries。

    open PASSWD, "/etc/passwd";
    while(<PASSWD>) {
        @fields = split /:/;
        ($first, $last) = split (/\s/, $fields[4]);
        $last = uc $last;
        $fields[4] = "$last $first";
        push @newEntries, join(':', @fields);
    }
    

    【讨论】:

    • 短:$fields[4] = "\U$last\E $first";,跳过手册uc。好吧,无论如何它是等价的......
    • 在空白或单字 GECOS 字段中添加空格。逗号分隔的失败。
    • +1 好点...我过于关注具体的例子,没有把它概括。
    【解决方案5】:
    $ awk -F":" ' { split($5,a," ");$5=toupper(a[2])" "a[1] } 1 ' OFS=":" /etc/passwd
    jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh
    

    【讨论】:

    • 以逗号分隔的 GECOS 字段失败。如果 GECOS 字段仅包含一个单词,则在其开头添加一个空格。
    • 我们在这里不要太过分了。
    【解决方案6】:
    $ perl -pe 's/^((.*:){4})(\S+)\s+(\S+?):/$1\U$4\E, $3:/' \
        /etc/passwd >/tmp/newpasswd

    要根据lastlog仅重写过去六十天内登录的用户,您可以使用

    #! /usr/bin/perl -n
    
    use warnings;
    use strict;
    
    use Date::Parse;
    
    my($user) = /^([^:]+):/;
    die "$0: $ARGV:$.: no user\n" unless defined $user;
    
    if ($user eq "+") {
      next;
    }
    
    my $lastlog = (split " ", `lastlog -u "$user" | sed 1d`, 4)[-1];
    die "$0: $ARGV:$.: lastlog -u $user failed\n"
      unless defined $lastlog;
    
    my $time_t = str2time $lastlog;
    
    # rewrites users who've never logged in
    #next if defined $time_t && $time_t < ($^T - 60 * 24 * 60 * 60);
    
    # users who have logged in within the last sixty days
    next unless defined $time_t && $time_t >= ($^T - 60 * 24 * 60 * 60);
    
    s/^((.*:){4})(\S+)\s+(\S+?):/$1\U$4\E, $3:/;
    print;
    

    $ ./fixusers /etc/passwd >/tmp/fixedusers
    

    【讨论】:

    • 逗号分隔的 GECOS 字段失败。
    • @Dennis 对于 paul44 在问题中未提及的许多格式,它可能会失败。
    • 我已经尽我所能清理了 GECOS 字段 - 这对我来说现在很好 - 谢谢。顺便说一句 - 有没有一种简单的方法来获取第一个字段(用户名)并判断他是否最近登录过(例如,与 lastlog -u 相比)并且仅在用户登录时才输出,例如过去 60 天,所以要剔除闲置 2 个月的用户?
    • 最后一个日志的格式如下。 perl 是否有一种时髦的方式来检查上次登录日期并排除任何 60 天未登录或未登录的人? [root@server1 paul]# lastlog -u bsmith 用户名端口来自最新 bsmith pts/2 172.21.83.181 Mon Oct 19 16:10:56 +0100 2009 [root@itdev5 paul]# lastlog -u jbloggs 用户名端口来自最新 jbloggs 从未登录过以上所有回复都非常有用 - 只是最后一点,请 - 干杯。
    • @paul44 看到“重写从未登录过的用户”的评论了吗?取消注释以下行,然后将“next unless...”注释掉几行。
    猜你喜欢
    • 2017-05-01
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    相关资源
    最近更新 更多