注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

awk及bash一些特性  

2013-07-15 00:14:16|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、bash中算数表达式
有时候还是想在bash使用算数表达式,而且还是希望像C语言中的if条件一样返回一个true表示满足条件了(还是C语言对我们的“毒害”深啊,啥都要死气白咧的网上靠)。这里有一个小问题,在shell中,如果一个命令的返回值为非零,说明程序执行失败了,如果返回值为零才是修成正果,此时对于if的算数表达式还是要进行一次转换,转换为bash可以识别的通用语言。
bash中的处理:
#if defined (DPAREN_ARITHMETIC)
static int
execute_arith_command (arith_command)
     ARITH_COM *arith_command;
……
  if (new)
    {
      exp = new->next ? string_list (new) : new->word->word;
      expresult = evalexp (exp, &expok);
      line_number = save_line_number;
      if (exp != new->word->word)
    free (exp);
      dispose_words (new);
    }
  else
    {
      expresult = 0;
      expok = 1;
    }

  if (expok == 0)
    return (EXECUTION_FAILURE);

  return (expresult == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
二、gawk中的正则表达式匹配
1、匹配整条记录
在awk中, /regexp/ 是有专门的解析规则它,它和通过'~'以及·!~·匹配的正则表达式不同,后两者事实上是一个双目运算符,在表达式的两侧都有操作数,而对于单个的匹配,它是单目运算符,它默认匹配的内容就是整个记录(record),
regexp
    /*
     * In this rule, want_regexp tells yylex that the next thing
     * is a regexp so it should read up to the closing slash.
     */
    : a_slash
        { ++want_regexp; }
      REGEXP    /* The terminating '/' is consumed by yylex(). */
……
          $$ = $3;
          $$->opcode = Op_match_rec;
          $$->memory = n;
        }
该操作的定义处注释说明为
Op_match_rec,        /* match $0 */
2、匹配表达式
这个匹配特指通过 ~ 和 !~表示的匹配动作,这个匹配在perl中也是如此表示的,在bash中大致也是如此,但是为了和 !~格式看起来统一,bash中的完成匹配使用的是 =~而不是一个单单的裸露的~操作符。其实这个操作符命名还是很文艺的,因为这个字符本来就是作为约等的概念,而正则表达式也是在一定条件下想等而不是通常意义上的==。但是遗憾的是awk并不支持=~这种看起来比较统一的操作符,只支持单目单字符的~来作为正则表达式的匹配,可能这样是为了节省输入吧,因为大部分情况下都是进行匹配的,而省略掉一个字符可以少输入一些字符(浅薄了)。
这个匹配在C语言里是没有的,大家看起来可能有些陌生,但是它事实上是和我们最为熟稔的==意义相同,所以大家可以放心使用。
三、awk对==表达式的处理
1、想等的判断
由于一个变量即可以作为数字,也可以作为字符串,所以在执行这个操作的时候,awk是如何判断是否相等呢?当然,如果两者是字符串想等,那么他们一定是数值相等的。但是可能有些数字是十六进制表示的,有些是十进制表示的,所以字符串不等不代表数字不等。
int
cmp_nodes(NODE *t1, NODE *t2)
{
    int ret = 0;
    size_t len1, len2;
    int l, ldiff;

    if (t1 == t2)
        return 0;

    if (t1->flags & MAYBE_NUM)
        (void) force_number(t1);
    if (t2->flags & MAYBE_NUM)
        (void) force_number(t2);
    if ((t1->flags & NUMBER) && (t2->flags & NUMBER)) {
        if (t1->numbr == t2->numbr)
            ret = 0;
        /* don't subtract, in case one or both are infinite */
        else if (t1->numbr < t2->numbr)
            ret = -1;
        else
            ret = 1;
        return ret;
    }

    (void) force_string(t1);
    (void) force_string(t2);
……
}
如果两者都是或者可能是数字,才进行数值比较;反之,如果一方是字符串,那么一定是字符串比较。对于大家最为常见的
if ($n == "somestring")
这里一定是进行字符串比较的。
那我们看一下默认的field节点的属性field.c文件中
static void
set_field(long num,
    char *str,
    long len,
    NODE *dummy ATTRIBUTE_UNUSED)    /* just to make interface same as set_element */
{
    NODE *n;

    if (num > nf_high_water)
        grow_fields_arr(num);
    n = fields_arr[num];
    n->stptr = str;
    n->stlen = len;
    n->flags = (STRCUR|STRING|MAYBE_NUM|FIELD);
}
这意味着field的比较是进行的数值比较,而对于无法转换为数字的字符串,转换结果都为零,所以如果进行field比较,那通常都是想等的。试了一下,和预期的不一样
[root@Harry gawk-4.1.0]# cat /etc/aliases | grep -ve '^$' -e '$#'| awk -F: '{ if ($1 == $2) print ; }'
[root@Harry gawk-4.1.0]# cat /etc/aliases | grep -ve '^$' -e '^#' | more
mailer-daemon:    postmaster
postmaster:    root
bin:        root
daemon:        root
adm:        root
lp:        root
sync:        root
shutdown:    root
halt:        root
mail:        root
news:        root
uucp:        root
operator:    root
games:        roo
2、再继续看看awk的实现
在cmp_nodes函数的force_number函数实现中,其中对于数字的转换是比较谨慎的。它并没有粗暴的将不识别的字符转换为零,而是将合法的数字字符串转换为零。对于那些无法转换为数字的字符串,此时同样不会置位NUMBER标志(flags)。
r_force_number(NODE *n)
    if (! do_posix) {
        if (isalpha((unsigned char) *cp)) { 对于上面的例子,执行流在这里返回,没有置位NUMBER flags
            return 0.0;
        } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) {
            if (n->flags & MAYBE_NUM)
                n->flags &= ~MAYBE_NUM;
            n->flags |= NUMBER|NUMCUR;
            n->numbr = get_ieee_magic_val(n->stptr);

            return n->numbr;
        }
        /* else
            fall through */
    }
四、awk中的关联数组
在awk中,数组始终是关联数组,也就是下表subscript始终会被转换字符串的形式,通过字串的hash来进行关联。这样的优点也比较明显,可以通过将不同维度的字符串数值拼接之后作为下表,从而可以使用一维数组模拟多维数组。
例如一个输入文件是
用户 行为 数量
我们就可以以第一列和第二列组成一个字符串下表,进而统计双键值的用户行为数量。
 五、bash中命令展开
在bash中,早期的命令展开使用的是反引号进行变量展开。但是之后大家就发现反引号有一个问题,虽然它输入方便(相同按键点击两次即可),但是正是由于开始和结束字符相同,所以不利于嵌套。反而,通过$(command)形式组成的替换形式能够进行任意层次的嵌套,所以在复杂命令中,括号形式的替换使用要比反引号使用的频率多得多。
  评论这张
 
阅读(707)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017