首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
拉勾
V2EX  ›  程序员

今天有个小需求,用 awk 和 sed 貌似不能很好的完成任务,打算转战 perl 了

  •  
  •   hujianxin · 13 天前 · 3667 次点击

    有一个文件,a.txt ,文容如下:

    0689LM  name=S:321, password=154adfv839473, value=37461278216723
    1633LM  name=S:241, password=15sdf78839611, value=21
    1121LM  name=S:673, password=1543978839612, value=1230
    1212LM  name=S:854, password=fd43978df9621, value=383242544
    3323LM  name=S:456, password=sd9788396sdd3, value=11211
    3322LM  name=S:234, password=dsfas78839623, value=23121
    4238LM  name=S:254, password=1fdsafdsafdsa, value=324342
    51LM    name=S:903, password=fdsafsdafsdaf, value=43543
    2389LM  name=S:572, password=fdsfdsdfwefff, value=2343235
    91452LM name=S:842, password=sdfwef23dffds, value=3434324
    9553LM  name=S:fed, password=23fdssfdss32f, value=10501342176
    

    需求:输出第一列的反转,name=S:之后的数字,value 之后的数值,如下:

    ML9860 321 37461278216723
    ML3361 241 21
    ML1211 673 1230
    ML2121 854 383242544
    ML3233 456 11211
    ML2233 234 23121
    ML8324 254 324342
    ML15 903 43543
    ML9832 572 2343235
    ML25419 842 3434324
    ML3559 fed 10501342176
    

    awk,不好实现第一列的反转

    sed,不好显示抓取 name=S:之后的数字(需要非贪婪)

    当然,也可能是我才疏学浅,如果各位大佬有更好的解决方法,欢迎指教。

    perl -aE '/name=S:(.*?),.*?value=(.*)/; $a = reverse $F[0]; say "$a $1 $2"' a.txt

    第 1 条附言  ·  12 天前
    题目中所提工具有:awk、sed、perl。

    如果有人觉着这些东西落伍了,不够好(比如你觉着写 excel 更好、python 更好),那么你把代码提出来,大家一起讨论一下。简单的来说,就是 show me your code。

    另外,题目中并没有提 shell 这门语言,如果你想喷想疯了的话,自己开一个帖子去喷 shell 如何?
    86 回复  |  直到 2018-12-07 14:44:43 +08:00
        1
    liprais   13 天前
    想用 perl 就用 perl 呗
    反正之后你会发现三十秒看不懂的东西都得重写
        2
    hujianxin   13 天前
    @liprais oneliner 从来都不是为了复用的啊
        3
    zbinlin   13 天前
    只用 sed 就可以呀:

    sed -r 's/^([[:digit:]]+)([A-Z]+)\s+name=S:([^,]+),.+value=(.+)$/\2\1 \3 \4/'
        4
    zbinlin   13 天前
    @zbinlin 呃,没看到第一列的数字反转了
        5
    hujianxin   13 天前
    @zbinlin 这个反转,估计用 sed 的保持空间协助一下也能搞定,但是太难了
        6
    yiyiwa   13 天前
    这样有点慢啊

    awk -F '[: ,]' '{"rev<<<"$1|getline a;print a,$3,$NF}'
        7
    yiyiwa   13 天前
    少个+,
    awk -F '[: ,]+' '{"rev<<<"$1|getline a;print a,$3,$NF}'
        8
    hujianxin   13 天前
    @yiyiwa 这条语句在我这里有语法错误呢

    sh: 1: Syntax error: redirection unexpected
    name=S value=37461278216723
    sh: 1: Syntax error: redirection unexpected
    name=S value=21
    sh: 1: Syntax error: redirection unexpected
    name=S value=1230
    sh: 1: Syntax error: redirection unexpected
    name=S value=383242544
    sh: 1: Syntax error: redirection unexpected
    name=S value=11211
    sh: 1: Syntax error: redirection unexpected
    name=S value=23121
    sh: 1: Syntax error: redirection unexpected
    name=S value=324342
    sh: 1: Syntax error: redirection unexpected
    value=43543
    sh: 1: Syntax error: redirection unexpected
    name=S value=2343235
    sh: 1: Syntax error: redirection unexpected
    842 value=3434324
    sh: 1: Syntax error: redirection unexpected
        9
    hujianxin   13 天前
    @yiyiwa 还是同样的错误
        10
    yiyiwa   13 天前
    awk -F '[: ,=]+' '{s=length($1);for(i=0;i<s;i++)k=k""substr($1,s-i,1);print k,$4,$NF;k=j}'
        11
    rrfeng   13 天前
    翻转第一列:

    echo .... | while read a b; do echo $(rev <<< $a) $b;done

    如果只有一行的话不需要 while

    echo .... | { read a b; echo $(rev <<< $a) $b; }
        12
    weyou   13 天前 via Android
    首先 awk 是可以用循环来反转的,就是麻烦点。第二,rev 命令很多系统都是自带的吧,为什么不用
        13
    rrfeng   13 天前
    @yiyiwa
    需要关闭管道,在处理行尾加上 close "rev<<<" 试试
        14
    ywgx   13 天前   ♥ 1
    对 vimer 来说,也就是录制个宏,播放一下而已
        15
    hcymk2   13 天前
    awk 可以自定义函数。
        16
    hujianxin   13 天前
    @yiyiwa 这个我也想到啦,但是稍微有点复杂了,长了一些。
    @ywgx 感觉分工还不太一样

    @hcymk2 自定义函数就不是一行了。。
        17
    codechaser   13 天前 via Android
    你们说的我根本看不懂……
        18
    likuku   13 天前
    额... 我是突然点进了今天的 黑魔法 9 级课程了么?

    别说 awk sed 了,shell 开始需要用到 判断 语句 我都果断直接用 py3 了。
        19
    zbinlin   13 天前   ♥ 1
    @zbinlin

    paste -d' ' <(sed -rn 's/^([^ ]+*).+$/\1/p' <filename> | rev) <(sed -rn 's/^.+name=S:([^,]+),.+value=(.+)$/\1 \2/p' <filename>)
        20
    ToT   13 天前
    能用 pandas 么。。。。
        21
    xpresslink   13 天前
    随便用个编程语言打开文件逐行遍历一下就可以。
        22
    DAPTX4869   13 天前
    shell 这堆玩意....瞎了
        23
    realpg   13 天前
    python 不好么
    其实更好的解决方案是 EXCEL
        24
    livepps   13 天前 via Android
    python 分分钟的事情
        25
    agagega   13 天前
    我想了想用 Ruby,也可以一行搞定,结果可能可读性比你的 Perl 稍微高点点,也没差太多...
        26
    yue9944882   13 天前   ♥ 2
    写 perl 很多年了。。perl 现在实战的价值就是所谓 one-liner。。谁写的更精简谁就更好,不管可读性。。这也是 perl 同 python 等其他“更高级”的语言的一个优势吧。。有点像所谓降维打击哈哈哈

    (不要学 perl 了,浪费时间)
        27
    congeec   13 天前   ♥ 1
    #23 说的对
    我都复制下来,放 excel 里
    写正则还要查一会儿语法
        28
    Tony042   12 天前
    @congeec 放 excel 里怎么实现呢,能大概讲讲吗
        29
    thedrwu   12 天前 via Android
    perl 作为主力脚本语言十多年,自从因为 numpy 用了 Python,发现回不去 perl 了:
    连 foreach 都不会写了,
    哪些操作会影响$_也忘了。
    嗯?这里的引用要套几层?
    从前的我怎么到处都在用 qw,那是干啥的现在怎么想不起来了…
    重新学 perl 好麻烦,还是退回 bash+sed+awk 算了。
        30
    congeec   12 天前
    @Tony042 假设你用 mac, 用 pbcopy/pbpaste 来复制粘贴。windows 也有类似的工具,不过你要自己找
    tips:column 是个很好用的表格格式化命令
        31
    zxcvsh   12 天前 via iPhone
    @ToT 你怎么会想到用 pandas 的
        32
    Mithrandir   12 天前
    为啥不用 python,或者 awk + sed + python -c
        33
    swordne   12 天前
    为啥我觉得这东西在 excel 里就能完成了?
        34
    bellucci1964   12 天前
    说 python 就算了,说 excel 的又是什么鬼,ssh 到远程主机上哪来的 excel
        35
    julypanda   12 天前
    不会发图,手动表情
    “这就是强者的世界吗?”
        36
    hujianxin   12 天前
    不会发图,手动表情
    “ talk is cheap, show me your code or excel picture ”
        37
    thecon   12 天前
    正解,oneliner 目前还没有什么能比 perl 更趁手的吧
        38
    bucuoo   12 天前
    awk 版本
    awk '{gsub(/[[:digit:]]+/,"&=",$1); split($1,aa,"="); split($2,na,"[=:,]"); split($4,va,"="); print aa[2]aa[1]" "na[3]" "va[2]}' a.txt
        39
    hujianxin   12 天前
    @thedrwu 老哥,都用了 9 年了,这么快就忘了啊
        40
    hujianxin   12 天前
    @thecon
    简单的任务,awk 更简洁,输入的字符更少,比如我需要看 nginx 日志里面的服务器错误的 log,只需要:awk '$4>=500'。
    但是复杂点的任务,比如我题目中提到的,perl 又更简洁很多,毕竟提供了功能强大的函数。
    如果再复杂的任务,谁还用 oneliner 啊,哈哈
        41
    hujianxin   12 天前
    @bucuoo
        42
    qinrui   12 天前
    反转是什么鬼?为什么要反转
        44
    operawang   12 天前
    文本处理当然是 Perl 当家
        45
    jjianwen68   12 天前
    写不来这么复杂的 shell
        46
    xiaket   12 天前
    ```{~}cat a.txt | python3 -c "import sys; \
    > seg1 = lambda line: ''.join(reversed(line.split()[0])); \
    > seg2 = lambda line: line.split()[1].split(':')[1].rstrip(','); \
    > seg3 = lambda line: line.split()[-1].split('=')[-1]; \
    > print(''.join(' '.join([seg1(line), seg2(line), seg3(line), '\n']) for line in sys.stdin.readlines()))"```

    好吧, 这么纠结的需求要放进一行,Python 的效果也一般.
        47
    msg7086   12 天前
    喜欢 Perl 处理文本的可以试试 Ruby ……
        48
    buhi   12 天前
    真的是把 shell 这坨狗屎当成宝了...
        49
    ecmascript2020   12 天前
    用 javascript
        50
    xingheng   12 天前
    @buhi 头一次看到有人讽刺 shell 是一坨屎的
        51
    xingheng   12 天前
    用 awk 和 shell 可以实现:

    先分片,找到所有期望的元素:
    awk -F '[[:space:]]+|:|,|=' '{ print $1" "$4" "$10 }'

    然后用 rev 实现反转:
    awk '{ system("echo $(echo "$1" | rev) " ); }'

    拼起来:
    awk -F '[[:space:]]+|:|,|=' '{ system("echo $(echo "$1" | rev) " $4" "$10); }'

    看了一下我的环境,rev 是 BSD 的命令,Linux 和 macOS 都内置
        52
    no1xsyzy   12 天前
    @xingheng 那么你见得少了,shell 作为编程语言确实是一坨屎
    脑子混乱根本不知道自己在干什么的感觉
    shell 就只是个壳罢了。
    如果不需要调用其他程序我一般还是 python 的。
        53
    hujianxin   12 天前
    @no1xsyzy 这个可能是与智商有关系把,我用 shell 很顺手,脑子也很清晰
        54
    buhi   12 天前
    shell 从各方面来说作为一个编程语言都是不合格的, 任何正常的编程语言, 包括被人当成梗鄙视的 php, 都能吊打 shell 七条街, 这个就是屎的定义
        55
    hujianxin   12 天前
    @xingheng 很赞,学习了
        56
    Vegetable   12 天前
    工具是在进步的吧,没必要虐待自己不是?
        57
    hujianxin   12 天前
    @buhi 来来,用你先进的工具完成以下题目的问题如何?
        58
    hujianxin   12 天前   ♥ 1
    @buhi 并没有题目中有提 shell 语言吗?提的时 awk、sed、perl。你是不是想喷人想疯了?搞一个假想敌就开始喷
        59
    araraloren   12 天前
    cat **FILE** | perl6 -ne '/ ^ (.*?) \s+ .* \:(\w+) .* "value=" (\d+) $/; say ($0.comb.reverse.join, $1, $2).join("\t")'
    你可以一行行的处理,perl 同理。。
        60
    MarioLuisGarcia   12 天前
    vim macro
        61
    hawhaw   12 天前
    perl 还是太老了
    但这货实在是强大
    几乎啥场景都能找到包
    直接用
        62
    urmyfaith   12 天前
    ![]( )

    你们要到 Excel
        63
    ltux   12 天前
    这种问题本来就是 Perl 的强项,awk sed 之类的工具本来压根本就不用学,一门 Perl 走天下即可。 上面说写了后来看不懂的,你写 sed awk 过几天照样看不懂。
        64
    hujianxin   12 天前
        65
    loopfor   12 天前
    我觉得这种还是 java/c#/c++顺手。
    那些一两行的看似精简,但是可维护性可读性都很差呀,出点问题调试也很麻烦。
        66
    araraloren   12 天前
    @loopfor 但是不是所有东西都需要维护的。。这东西就是用完就扔,下次再写
        67
    yiyizym   12 天前
    用 awk,自定义一个反转函数:rev

    awk 'function rev(str){if(str == "") return ""; else return (rev(substr(str,2)) substr(str,1,1))} {print rev($1), substr($2,8), substr($4,7)}' a.txt
        68
    hujianxin   12 天前
    @yiyizym 可惜 awk 没有自带一个这种函数
        69
    yiyizym   12 天前
    @yiyizym

    第二列多了个逗号,而且把 fed 也算进去了,更正一下:
    awk 'function rev(str){if(str == "") return ""; else return (rev(substr(str,2)) substr(str,1,1))} {print rev($1), gensub(/[^0-9]/,"","g",$2), substr($4,7)}' a.txt
        70
    hellolleh   12 天前
    why not python?
        71
    lululau   12 天前   ♥ 2
    说 Python 的可以试试能不能一分钟之内写出代码来,是的,像这样的不太复杂的文本处理任务用 Perl 一分钟之内就可以写出来代码

    说 Excel 的可以试试算个一千万行或者更大的文本试试?

    Perl 相比 sed / awk 的优势是,你不许区分 perl 还是 gperl,不许记哪些选项和功能是只有 GNU 版本的才有,不需要去区分 basic regex, extended regex, posix regex,你只要会 Perl 正则就可以了

    sed 算是门艺术,偶尔写一下可以陶冶情操,贴一个 sed 实现 rev 的代码,陶冶一下😀

    sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//'
        72
    hujianxin   12 天前
    @lululau 这个很陶冶情操,手工再见
        73
    Raymon111111   12 天前
    哎呀 java 也可以其实(手动斜眼
        74
    no1xsyzy   12 天前   ♥ 1
    @hujianxin “脑子混乱”并不是指我,而是写出 shell 的人。
    一会想要以简短的字符表明一个意思,比如$+一个字符的那些(这不查那大部头 manual 根本不知道意思);
    一会又不允许简洁地表达,比如单引号的处理(如何在单引号的引用字符串内放一个单引号?先结束前一段单引号字符串,反斜杠转义一个单引号进去,再开启下一段单引号字符,1->4 比反引号地狱还快)。

    这纯是由于 shell 根本不是固定的一个人写的,甚至不是为了同一个目的设计的特性。
    毫无主见地随波逐流地写特性出来。
    只因为“之前有这个特性所以我们还是要保留这个特性”。
    向后兼容只会造成过去的麻烦依旧堆积在那边。
        75
    hujianxin   12 天前
    @no1xsyzy oneliner 的话,没有这种问题,因为它根本不需要维护,用完就扔了
        76
    no1xsyzy   12 天前
    @lululau ……能,连 re 都不用
    x=input()之后用 split 爆,一行内分别是
    ''.join(reversed(x.split(" ")[0]))
    x.split("name=S:")[1].split(",")[0]
    x.split(", value=")[-1]
    用 print 输出
    然后因为任意多行的要求,整个框在 while True 里面
    总共写了 40 多秒
        77
    no1xsyzy   12 天前
    @hujianxin 那就不是“ shell 作为编程语言”的情况
    shell 的 oneliner 并不能处理什么东西
        78
    zhuangjia   12 天前
    @xingheng 学习了
        79
    shyrock   12 天前
    @lululau emmm。。。这比较方法。。。是 m20p 比冰箱的显示分辨率高的比法?
        80
    Kirscheis   12 天前   ♥ 1
    import re

    n = re.compile(r'''name=S:(.{3})''')
    m = re.compile(r'''value=(\d*)''')

    with open('asdf','r') as f:
    s = f.readlines()

    for i in s:
    a,b,c,d = i.split()
    a = a[::-1]
    b = n.match(b).groups()[0]
    d = m.match(d).groups()[0]
    print(a, b ,d)


    python,写了 30 秒左右吧。

    不是说 perl 这些不好,我个人认为新的这些脚本语言的一个优点是,即使没有接触过编程的人,也能大概猜出来它想干什么,因此在过些年过气之后,不至于让大家都觉得遗留的脚本是天书。
        81
    aliipay   12 天前
    @shyrock 差不多吧,所以正确方法应该是选取一个合适的工具。
        82
    lululau   12 天前 via iPhone
    学开汽车比学开飞机容易,所以汽车比飞机好,见识短没关系,但是保持开放的心态,先了解再判断
        83
    no1xsyzy   12 天前
    @Kirscheis 不至于让大家都觉得遗留的脚本是天书+1
    这就是我为什么会反对 python 里加 “:=”
        84
    ps1aniuge   11 天前
    #建议保存编码为:bom 头 + utf8

    $输入文件 = 'a:\pscode\TEMP_2018\temp207\aaa.txt'
    $输出文件 = 'a:\pscode\TEMP_2018\temp207\aaa2.txt'

    function 翻转函数($a)
    {
    $b = $a.tochararray()
    [System.Array]::Reverse($b)
    [string]$c = [system.String]::Concat($b)
    return $c
    }

    $所有行 = Get-Content -LiteralPath $输入文件 -ReadCount 0
    $输出的所有行 = @()
    foreach ($行 in $所有行)
    {
    $列 = $行.split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)
    $列 1 = 翻转函数 $列[0]
    $列 2 = $列[1].replace('name=S:','').trimend(',')
    $列 3 = $列[3].replace('value=','')
    $新列 = $列 1,$列 2,$列 3 -join ' '
    $输出的所有行 += $新列
    }
    Set-Content -LiteralPath $输出文件 -Encoding UTF8 -Value $输出的所有行

    powrshell 代码,win,linux 通用。
    源文件不要有尾部空行。
    测试通过。
        85
    ps1aniuge   11 天前
    $列 3 被 V2EX 人为分词了,不怪我
        86
    ps1aniuge   11 天前
    用法:
    在 linux 的 bash 中,用我分享的 powershell 脚本的方法:(需要先安装 linux 版 powershell )
    /usr/bin/pwsh -f "fz 翻转字符串.ps1" -输入字符串 'abcd'



    #建议保存编码为:bom 头 + utf8
    param
    (
    [string]$输入字符串
    )

    $b = $输入字符串.tochararray()
    [System.Array]::Reverse($b)
    $c = [system.String]::Concat($b)
    return $c
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1146 人在线   最高记录 4019   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.1 · 24ms · UTC 17:36 · PVG 01:36 · LAX 09:36 · JFK 12:36
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1