【问题标题】:Add item to comma separated list with one-liner使用单行将项目添加到逗号分隔列表
【发布时间】:2016-11-09 23:37:55
【问题描述】:

我想向代表逗号分隔列表的字符串变量添加一个新项目,同时防止项目出现两次。

我想出了这个 python 解决方案:

python -c "import sys ; first = set(sys.argv[1].split(',')) ; all = first.union(set(sys.argv[2:])) ; print ','.join(all)" 1,2 4 3 2
1,3,2,4

多行:

import sys
first = set(sys.argv[1].split(','))
all = first.union(set(sys.argv[2:]))
print ','.join(all)

虽然该解决方案有效,但它不太适合 shell 脚本中的单行。

我已经使用awksed 或普通bash 尝试了几件事,但没有任何结果可以得出一个简短但仍然稳定的解决方案。有人知道如何用简短的命令行习语来表达吗?

【问题讨论】:

    标签: bash shell csv awk sed


    【解决方案1】:

    Perl 解决方案:

    perl -le 'undef @h{ (split /,/, shift), @ARGV }; $, = ","; print keys %h' 1,2 4 3 2
    

    【讨论】:

      【解决方案2】:

      使用sed:

      echo 1,2 3 2 4 | sed -r ':a {s/([^, ]+)(.*)\1/\2,\1/;ta}; s/ /,/g; s/,+/,/g; s/^,//'
      

      【讨论】:

      • echo 5,4 3 4 5 | sed ... 输出,3,4,5
      • @JamesBrown 感谢您的提示。
      【解决方案3】:

      awk 来救援!

      $ echo -n "1,2 4 3 2" | awk -v RS='[, ]' '!a[$0]++' | paste -sd,
      
      1,2,4,3
      

      【讨论】:

        【解决方案4】:

        它不是单行的,但 imo 仍然是一个干净的 shell 解决方案:

        #!/bin/bash
        list="1,2"
        new_items="4 5 3"
        
        for i in $new_items ; do
            # The `\b` does match at word boundaries
            ! grep -Eq "\b$i\b" <<< "$list" && list="$list,$i"
        done
        echo "$list"
        

        如果你想要一个单行,你可以把它放到一个函数中:

        function add_items() {
            list="$1"
            new_items="$2"
            for i in $new_items ; do
                ! grep -Eq "\b$i\b" <<< "$list" && list="$list,$i"
            done
            echo "$list"
        }
        

        这样称呼:

        add_items '1,2' '1 2 3'
        

        【讨论】:

          【解决方案5】:

          bash 选项:将项目列表保存在字符串变量(下面的list)中,并带有前导和尾随逗号。这样,每个项目都以逗号开头和结尾,这使事情变得更加容易。添加项目:

          new_item=42   # or whatever
          list="${list//,${new_item},/,}${new_item},"
          

          ${list//...} 删除所有重复项(// 表示全局替换),然后${new_item}, 将新项目粘贴到列表的末尾。添加多个项目:

          list=",1,2,"
          for x in 4 3 2 ; do list="${list//,$x,/,}$x," ; done
          

          要将逗号从列表中删除,您可以使用

          list="${list#,}"
          list="${list%,}"
          

          上面的具体示例的单线,但扩展了更多测试用例,将是:

          bash -c 'list=",$1,"; shift; for f in "$@"; do list="${list//,$f,/,}$f,"; done; list="${list#,}"; list="${list%,}"; echo $list' -- 1,2 3 4 44 444 1 2
          

          但请注意,该行的大部分内容只是从参数中获取列表,然后输出到标准输出。在脚本中,list="${list//,$x,/,}$x," 就是您所需要的。

          【讨论】:

            【解决方案6】:

            更多 AWK:

            $ cat test.in
            1,2 3 2 4
            5,4 3 4 5
            
            $ cat test.in|awk 'BEGIN {FS="[ ,]";OFS=","} {delete a; delete b; n=split($0,a,FS); for(i in a) b[a[i]]=n--; for(i in b) printf "%s%s",i,(b[i]>1)?OFS:ORS}'
            1,2,3,4
            3,4,5
            

            【讨论】:

            • 这取决于b的索引顺序对应a的索引顺序w.r.t。打印OFSRS,但情况并非总是如此。例如,当我用echo '5,4,3 2 1 5 6' | awk ... 调用你的程序时,我得到2,3,4,5&lt;\n&gt;6,1,
            • 嗯,我不知道,我得到1,2,3,4,5,6。我正在运行 gawn 和 mawk。你在做什么(不是双关语)?
            • :-) 带有本机 bsd awk 的 Mac OS X。使用 gawk 我之前的示例没有问题,但它确实可以使用 $ echo '5,4,3 9 8' | gawk ... 重现。这给了我3,4,5,8&lt;\n&gt;9,
            【解决方案7】:

            越来越多的 AWK:

            $ echo 1,2 3 2 4|awk 'BEGIN {RS="[ ,\n]";OFS=","} {a[$0]=++i} END {for (j in a) printf "%s%s", j, a[j]<NR?OFS:ORS}'
            1,2,3,4
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-09-27
              • 2016-03-30
              • 2017-12-08
              • 2013-07-03
              • 1970-01-01
              • 2019-09-24
              相关资源
              最近更新 更多