Board logo

标题: awk中使用gsub再配合|出現奇怪現象? [打印本页]

作者: pillow     时间: 2007-6-18 17:47    标题: awk中使用gsub再配合|出現奇怪現象?

想對一個內容類似下面的txt處理,最後得到AA_A這樣的形式,最開始是用-F指定不同FS去切開,後來想只通過gsub兩次將不需要字符替換成""即得到答案,但第一次替換完成後再用第二次時,通道符 | 被認成了文件,請問這個是bug還是我的用法不正確?
第二個問題是我想用通本符/
  • =/來代表/\"CN=/,但不成功,請問這種寫法是錯在哪里?


    "CN=AA A(中文名字1),OU=Users,OU=NNNN,DC=CCC,DC=aDDD"
    "CN=BB B(中文名字2),OU=Users,OU=PPPPP,DC=CCC,DC=aDDD"


    我的命令,|後面的我換成了一般的打印都會提示有錯
    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt | gawk -F"(" "{print $1}"

    gawk: (FILENAME=1.txt FNR=790) fatal: cannot open file `|' for reading (Invalid argument)

    [ Last edited by pillow on 2007-6-18 at 05:49 PM ]
    作者: lxmxn     时间: 2007-6-18 20:48

    gawk "BEGIN{FS=\"[=(]\"}{print $2}" 1.txt

    作者: pillow     时间: 2007-6-18 21:30


      Quote:
    Originally posted by lxmxn at 2007-6-18 20:48:
    gawk "BEGIN{FS=\"[=(]\"}{print $2}" 1.txt

    首先谢谢指导!

    我又找了找awk的贴子,但没发现关于您的用法的讲解,那么就大胆猜测一下,若不对,再请您更正:
    1.
    当FS=多个不同符号时,则任何一个独立的符号都是FS,而不是按指定的形式连在一起的才是。
    比如您用的是=( 那么就是说任何被=或(分开的都算是一个field,而不是被连着的=(才算,对吗?

    2.
    尽管对您的方法我已经有了推测理解,但对我问的问题仍有不解,我原来的命令是:
    gawk -F"(" "{ print $1 }" 1.txt |  gawk -F"=" "{if ($0 !~ /!/) {print $2}}"

    即显示(前面的部分 | 显示=后面不含!的部分,此时的 | 会正常工作,而没被gawk当作多个文件中的一个,而我最开始列出的命令中却不是这样,原因会是什么呢?

    [ Last edited by pillow on 2007-6-18 at 09:31 PM ]
    作者: lxmxn     时间: 2007-6-18 22:08
    Re pillow:

    1、如果要指定多个分隔符,要用中括号将分隔符括起来,否则gawk会认为这个字符串作为一个大的分隔符。
    echo 0123456789|gawk -F34 "{print $1,$2}"

    echo 0123456789|gawk -F[34] "{print $1,$2}"
    这两个例子输出的结果是不一样的。

    2、抱歉,第二个问题的原因我也不是很明白。
    作者: pillow     时间: 2007-6-18 22:14


      Quote:
    Originally posted by lxmxn at 2007-6-18 22:08:
    Re pillow:

    1、如果要指定多个分隔符,要用中括号将分隔符括起来,否则gawk会认为这个字符串作为一个大的分隔符。
    [code]
    echo 0123456789|gawk -F34 "{ ...

    这个问题有些想得通了,应该和linux下面的情况有些类似,比如[1,2,3,4] or [1,3-9]这样的用法吧,只不过没有用什么符号再将它们隔开有些让人意外。

    2看来只能让它随风而逝啦,或许是gawk windows的bug(阿Q一下:))。

    再次感谢lxmxn!

    [ Last edited by pillow on 2007-6-18 at 10:35 PM ]
    作者: pillow     时间: 2007-6-18 22:31
    看来还得麻烦您,
    为了将space换成undeline,得到最终的AA_A形式,我对pipe进行了gsub操作,追加了| gawk "{gsub(/ /,"__");print}",整行变为:

    gawk "BEGIN{FS=\"[=(,]\"} $2 !~ /[!#]/{print $2}" 1.txt | gawk "{gsub(/ /,"_");print}"

    但发现space没有换成undeline,而且消失了,预想中的AA_A没有出现,却变成了AAA,奇怪啊!
    作者: lxmxn     时间: 2007-6-19 02:15
    这样试试:
    gawk "BEGIN{FS=\"[=(,]\"} $2 !~ /[!#]/{print $2}" 1.txt | gawk "{gsub(/ /,\"_\");print}"

    作者: pillow     时间: 2007-6-19 11:40


      Quote:
    Originally posted by lxmxn at 2007-6-19 02:15:
    这样试试:
    gawk "BEGIN{FS=\"[=(,]\"} $2 !~ /[!#]/{print $2}" 1.txt | gawk "{gsub(/ /,\"_\");print}"

    這次徹底懂了,action script裏面的所有“都要加escape才行,
    受教了……
    作者: lxmxn     时间: 2007-6-19 19:14
    嗯,这是cmd和linux的shell的不同之处,只要注意这点,其它的都一样的。
    作者: qzwqzw     时间: 2007-6-20 12:26
    关于这个
    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt | gawk -F"(" "{print $1}"

    应该不难理解

    这主要还是引号的问题

    首先\是awk的转义符号,而不是cmd的

    其次cmd的引号匹配是成对的

    除非没有可以成对的引号

    上句中 1.txt 前的引号恰好是单数

    所以只能与 gawk -F后的引号匹配

    这导致 | 的管道作用被 "" 关闭

    也导致gawk后所有的字符串

    都被cmd视为连续的一个参数

    全部传给了第一个 gawk

    所以,最终的原因就是第一个 gsub 中多了或少了一个 \"

    至于怎么改,就需要根据上下文的代码进行调整了

    [ Last edited by qzwqzw on 2007-6-21 at 08:19 AM ]
    作者: lxmxn     时间: 2007-6-20 12:40
    在 qzwqzw 的解释下,修改了一下这句就可以了。
    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    作者: pillow     时间: 2007-6-21 00:49


      Quote:
    Originally posted by lxmxn at 2007-6-20 12:40:
    在 qzwqzw 的解释下,修改了一下这句就可以了。
    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    在对使用引号具体问题提问前,想先问下“CMD引号必须成对出现”这样的规则在什么地方有讲?(刚才网上小搜了一下,没找到,或者请指教我应在论坛中用哪个key word查询?)

    1.
    我在思想上理解您和qzwqzw的解释,但对1.txt后面那个"配对十分不解。
    按我先前认识中的理解,"  "中的东西是作为参数传给指令的,举例如下(先认为{[(是不存在的,引入它们只是想说明"的作用范围)
    cmd "{gawk "[ gsub( ..."....) ] "}"
    {}外面的一对"包起来的内容,是传给CMD的参数,
    []外面的一对"包起来的内容,是传给gawk的参数,
    ()外面的一对"包起来的内容,是传给gsub的参数,
    无论()[]{}哪个范围中的什么字符,都应在从内向外执行时失去其特殊意义,只将运行结果传给外层做下一级运算,按这个思想理解,最后就变成了
    gawk "{XXXXXXXX}" 1.txt" ,最后多出来的那个",反而是多出来的……

    2.
    按qzwqzw的解释,多加了个"的下面这句
    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    就会被正确的解释为
    gawk "{gsub(/“CN=/,"");print}" 1.txt | gawk -F"(" "{print $1}"
    这时这一句已经不再需要遵守CMD的"成双原则,即可将|认成是管道符,是吗?

    但cmd识别'配对的方法,我想了半天,还是没想懂。

    再请帮忙消除疑云,感谢!
    作者: lxmxn     时间: 2007-6-21 01:13
    RE pillow:

    1、

      Quote:
    在对使用引号具体问题提问前,想先问下“CMD引号必须成对出现”这样的规则在什么地方有讲?(刚才网上小搜了一下,没找到,或者请指教我应在论坛中用哪个key word查询?)

    有很多“不成文”的批处理技巧,都是微软的帮助文档和网上的教程没有的,是自己大量的实践和总结得出来的。

    2、

      Quote:
    我在思想上理解您和qzwqzw的解释,但对1.txt后面那个"配对十分不解。

    我是这样理解的:
    在命令行执行的每个命令,都需要经过命令解释器(cmd.exe)来预处理,其中包含对变量值的扩展,对特殊符号的转义等等,其中应该还包括对引号的匹配的检查。

    例如
    C:\>echo "batch|find "at"
    "batch|find "at"

    C:\>echo "batch"|find "at"
    "batch"
    我想出现这个问题的原因,还是由于引号”不配对造成的。

    3、

      Quote:
    gawk "{gsub(/“CN=/,"");print}" 1.txt | gawk -F"(" "{print $1}"
    这时这一句已经不再需要遵守CMD的"成双原则,即可将|认成是管道符,是吗?

    此时命令已经被cmd解释了一次,就不再受cmd的引号匹配的原则的约束,再来由gawk进行下一步的处理。所以此时管道符号|就可以正常发挥它的作用了。

    [ Last edited by lxmxn on 2007-6-21 at 01:30 AM ]
    作者: qzwqzw     时间: 2007-6-21 08:30
    回复一下pillow的问题

    首先,我并没有提到过“引号必须成对出现”的原则

    而且cmd里也不存在这条原则

    在其他语法解析器中很可能存在

    只是,在cmd中没有成对匹配的引号无法发挥起转义作用

    这在其他语法解析器中也是一样的
    -------------------------------------------------

    其次,cmd的引号的采用最近匹配原则,而非最远匹配原则

    也就是说在cmd中每个引号总是与离它最近的引号进行匹配

    gawk我并不熟悉

    不过据我所知

    大多数语法解析器都是采用与cmd相类似的原则

    所以也就是不存在你所说的“从内向外执行”特征

    而是从前到后,或者从左到右

    [ Last edited by qzwqzw on 2007-6-21 at 08:35 AM ]
    作者: pillow     时间: 2007-6-22 08:23
    这两个的区别我能理解出来了,但是“如何匹配还是没能搞懂,若许再需要实例去了解吧。

    于是有个问题,比如在lxmxn的例子中,改過後的命令後來已經是要先顯示"batch",再尋找at了吧?若我一定要求顯示的是"batch,再從這個字串中尋找at,那麼應該將"放在什麼地方?
    (不是在鑽牛角尖,是希望弄清楚配對的方式)

    C:\>echo "batch|find "at"
    "batch|find "at"
    作者: qzwqzw     时间: 2007-6-22 12:33
    使用^取消"的转义作用即可

      Quote:
    C:\>echo ^"batch | find "at"
    "batch


    作者: pillow     时间: 2007-6-23 13:22


      Quote:
    Originally posted by qzwqzw at 2007-6-22 12:33:
    使用^取消"的转义作用即可


    唉,都被"給搞蒙了,我還把^加到了“裏面……居然問出來這種白癡問題,
    不過透過這個問題,倒是把qzwqzw之前講的一句話理解了:
    “首先\是awk的转义符号,而不是cmd的;其次cmd的引号匹配是成对的”

    所以"CN中前面的"並沒有被他前面的\在cmd中escape,而只是在gawk中escape;
    因而CMD並沒找到和這個"匹配的",就出現了後面|失效的問題。

    新問題:
    雖然我不是專業編程,但上學時的書裏講的也的確是像qzwqzw講的那樣,“从前到后,或者从左到右",但您後來又有“也就是说在cmd中每个引号总是与离它最近的引号进行匹配”這樣的說明,就有疑問產生了,"CN中的"為什麼不是與(/\"CN=/,\"\")中逗號後面的"匹配,而是與1.txt後面的那一個匹配呢?
    換個問法也就是說,既然英文半角的"不會像全角下的“”這樣分左右,而又是從左向右的執行,解釋器是如何判定哪兩個是相互匹配的那一對?

    [ Last edited by pillow on 2007-6-23 at 02:01 PM ]
    作者: pillow     时间: 2007-6-25 08:21
    問得有些羅唆,只是知道"要放在|的前面,可是為什麼放在1.txt後面

    gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    "CN=/,\"\");print}" 1.txt"這一段為什麼不會對語句解釋產生岐義呢?
    作者: qzwqzw     时间: 2007-6-25 16:41


      Quote:
    "CN中的"為什麼不是與(/\"CN=/,\"\")中逗號後面的"匹配,而是與1.txt後面的那一個匹配呢?

    这个理解是错误的

    在cmd中,"CN中的"是与最前的"{gsub中的"相匹配的

    这就是所谓的“最近匹配原则”
    也可以理解为第(2*n)个的引号与(2*n-1)个引号相匹配(n为自然数)
    即所谓的“前单配后双”原则

    这种原则实现起来是最为简单的
    不需要使用栈来保存"的层级
    大约只需要做个开始与结束的标记就可以了

    遇到第一个引号标记引号串开始
    空格、Tab、分号等所有分界符和其它一些转义字符失效
    直到遇到第二个引号
    标记引号串结束
    分界符等重新生效
    如果再遇到第三个引号
    则分界符等再次失效
    直到下一个引号出现
    如此反复

    如果引号串之间没有出现分界符
    则所有引号串以及其间的串被认作一个串

    -----------------------

    对于这行gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    cmd首先将它理解为五个串

    1、gawk
    2、"{gsub(/\"CN=/,\"\");print}" 1.txt"
    3、|
    4、gawk
    5、-F"("
    6、"{print $1}"

    而最有疑义的第2个串是由下面几个子串组成的

    2-1、"{gsub(/\"
    2-2、CN=/,\
    2-3、"\"
    2-4、);print}
    2-5、" 1.txt"

    几个子串之间因为没有空格等分界符
    所以被认为是一个字符串
    并作为参数传给了gawk
    至于gawk如何分析这个串就是Gawk内部的事了
    -----------------------

    因此,据我的理解
    那个gawk句子也可以改作以下几种形式
    而不会让cmd产生误解

    gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt | gawk -F"(" "{print $1}"
    gawk {gsub(/\"CN=/,\"\");print} 1.txt" | gawk -F"(" "{print $1}"

    上面两句cmd仍然将gawk 到 | 之间的部分认作一个串
    下面两句cmd会则将它认作两个串,第二个串是 1.txt

    gawk "{gsub(/\"CN=/,\"\");print} 1.txt | gawk -F"(" "{print $1}"
    gawk {gsub(/\"CN=/,\"\");print}" 1.txt | gawk -F"(" "{print $1}"

    至于会让gawk是否会产生歧义
    因为没有测试环境不太肯定
    前面两句应该是没有什么问题的
    后面两句则可以存疑

    而这样的句子很可能是不行的

    gawk "{gsub(/\"CN=/, \"\");print}" 1.txt" | gawk -F"(" "{print $1}"

    因为逗号后多了一个空格
    而且这个空格正好位于2*n与2*n+1之间的引号中
    因此cmd将它视作参数分界符
    而将"{gsub(/\"CN=/, \"\");print}" 1.txt"分成了两个串

    2、"{gsub(/\"CN=/,
    2+1、\"\");print}" 1.txt"

    而Gawk应该是不支持将匹配模式写在两个串中的

    [ Last edited by qzwqzw on 2007-6-25 at 05:07 PM ]
    作者: lxmxn     时间: 2007-6-25 17:58


      Quote:
    Originally posted by qzwqzw at 2007-6-25 16:41:
    因此,据我的理解
    那个gawk句子也可以改作以下几种形式
    而不会让cmd产生误解

    gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt | gawk -F"(" "{print $1}"
    gawk {gsub(/\"CN=/,\"\");print} 1.txt" | gawk -F"(" "{print $1}"

    上面两句cmd仍然将gawk 到 | 之间的部分认作一个串
    下面两句cmd会则将它认作两个串,第二个串是 1.txt

    gawk "{gsub(/\"CN=/,\"\");print} 1.txt | gawk -F"(" "{print $1}"
    gawk {gsub(/\"CN=/,\"\");print}" 1.txt | gawk -F"(" "{print $1}"

    至于会让gawk是否会产生歧义
    因为没有测试环境不太肯定
    前面两句应该是没有什么问题的
    后面两句则可以存疑

    感谢 qzwqzw 的见解。

    我这里有环境测试,所以做了下面这几个测试:

      Quote:
    >>gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt | gawk -F"(" "{print $1}"
    gawk: {gsub(/"CN=/,"");print}"
    gawk:                        ^ unterminated string

    在gawk的所有参数两边加上"":

      Quote:
    >>gawk ""{gsub(/\"CN=/,\"\");print}"" 1.txt" | gawk -F"(" "{print $1}"
    AA A
    BB B



      Quote:
    >>gawk {gsub(/\"CN=/,\"\");print} 1.txt" | gawk -F"(" "{print $1}"
    AA A
    BB B

    测试成功。

      Quote:
    >>gawk "{gsub(/\"CN=/,\"\");print} 1.txt | gawk -F"(" "{print $1}"
    ^C^C

    这个运行时间长而没有结果,还是在gawk的整个参数两边加上"":

      Quote:
    >>gawk ""{gsub(/\"CN=/,\"\");print} 1.txt" | gawk -F"(" "{print $1}"
    AA A
    BB B



      Quote:
    >>gawk {gsub(/\"CN=/,\"\");print}" 1.txt | gawk -F"(" "{print $1}"
    ^C^C

    这个同样是运行时间长且没有结果,还是加"":

      Quote:
    >>gawk "{gsub(/\"CN=/,\"\");print}" 1.txt" | gawk -F"(" "{print $1}"
    AA A
    BB B

    具体其它的方案没有做过多的测试

    推荐这个贴子要好好看看
    http://bbs.chinaunix.net/viewthr ... p;page=4#pid1511745

    昨天看了一下,很有启发,其中提到了Hard Quote和Soft Quote,虽然是Linux的环境,但是对于理解命令行的解析执行还是很有启发性的。

    [ Last edited by lxmxn on 2007-6-25 at 06:00 PM ]
    作者: qzwqzw     时间: 2007-6-26 10:37
    ^ unterminated string的尖角号对的是上一行的print}后的"
    刚开始看了半天没看明白
    终于去下了个Gawk实际测试
    才反应过来是论坛的空格比英文字母要窄
    导致文本对齐的效果发生了变化
    ----------------------------------------------------------------

    ——“运行时间长且没有结果”
    这是在等待你的输入
    输入一些字符可以看到一些效果
    按Ctrl+Z和回车结束输入
    很显然,gawk没有从cmd传给它的命令行中找到要操作的文件
    所以才从标准输入端(也就是控制台中的键盘)读取文本
    ----------------------------------------------------------------

    对于出现问题的语句
    gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt | gawk -F"(" "{print $1}"

    从cmd角度分析,它将命令行以|为界限拆分成两句
    1、gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt(包括紧随其后的一个空格)
    2、 gawk -F"(" "{print $1}"

    然后cmd将gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt传递给了gawk
    而gawk则以不同的方式再次解析了这个串
    被\字符esacape掉的引号被忽略
    所以{gsub前的引号只有与print}后的引号匹配

    那么是第一个引号,还是第二个引号呢?
    看看这句
    gawk "{gsub(/\"CN=/,\"\");print}" " 1.txt
    gawk: cmd. line:1: fatal: cannot open file ` 1.txt ' for reading (No such file o
    r directory)

    很明显gawk匹配的是第一个引号
    也就是说它同cmd一样是采用的“最近匹配原则”
    事实上对于引号这种由同样字符构成的字符对
    采用“最远匹配原则”处理起来将是很麻烦的
    而且没有多少实际意义
    ----------------------------------------------------------------

    那么cmd与gawk的不同在哪里呢?
    他们的不同就在于gawk多忽略了三个由\前缀的引号
    从而导致字符串合并与切分发生了戏剧般的变化

    对于gawk "{gsub(/\"CN=/,\"\");print}"" 1.txt(包括紧随其后的一个空格)
    cmd把它分成
    1、gawk
    2、"{gsub(/\"CN=/,\"\");print}""
    3、1.txt

    而gawk把它分成
    1、gawk
    2、"{gsub(/\"CN=/,\"\");print}"" 1.txt
    由于第二个串中的 1.txt 缺少与前面引号相匹配的引号
    所以提示“未终止串”

    对gawk "{gsub(/\"CN=/,\"\");print}" " 1.txt
    gawk把它分成
    1、gawk
    2、"{gsub(/\"CN=/,\"\");print}"
    3、" 1.txt
    由于gawk从第三个串中取操作文件名
    而名字前面加空格的文件并不存在
    所以提示“不能打开文件”

    所以,下面的句子将是正确的
    gawk {gsub(/\"CN=/,\"\");print} "1.txt

    cmd中将此句分成
    1、gawk
    2、{gsub(/\"CN=/,\"\");print} "1.txt

    gawk则又分成
    1、gawk
    2、{gsub(/\"CN=/,\"\");print}
    3、"1.txt
    --------------------------------------------

    至于那篇帖子中的Hard Quote和Soft Quote
    那应该是Unix shell的概念
    跟gawk关系不大
    而cmd中就没有这样的概念

    [ Last edited by qzwqzw on 2007-6-26 at 10:51 AM ]
    作者: lxmxn     时间: 2007-6-27 01:54


      Quote:
    Originally posted by qzwqzw at 2007-6-26 10:37:
    ——“运行时间长且没有结果”
    这是在等待你的输入
    输入一些字符可以看到一些效果
    按Ctrl+Z和回车结束输入
    很显然,gawk没有从cmd传给它的命令行中找到要操作的文件
    所以才从标准输入端(也就是控制台中的键盘)读取文本

    嗯,的确是这样,当时没有想到它会接受标准输入,所以就妄加推断了。

    看了兄的分析之后,又明白了许多了,多谢。