【问题标题】:How to make column values to rows in unix如何将列值设置为unix中的行
【发布时间】:2018-06-14 19:08:18
【问题描述】:

以下是文件中的示例数据

4 列 TAB 与最后一列分隔为逗号分隔的值。 第 3 列实际上显示了第 4 列中的值的数量。

6338838 ESR 3   173812,10547556,10518181
6338822 ESR 2   7219086,12761162

预期输出:

6338838 ESR 3   173812
6338838 ESR 3   10547556
6338838 ESR 3   10518181
6338822 ESR 2   7219086
6338822 ESR 2   12761162

尝试使用 AWK ,但无法使其工作。

【问题讨论】:

  • 什么你尝试了awk
  • 可能有重复,但链接的不是其中之一!

标签: bash unix awk transpose


【解决方案1】:

编辑:简单地使用gsub在这里去掉逗号怎么样:)

awk -F" +" '{gsub(",",ORS $1 OFS $2 OFS $3 OFS,$4)} 1' Input_file | column -t

-F 更改为 -F"\t",以防您的 Input_file 以 TAB 分隔。



使用awk-F 并根据字段值进行打印怎么样?

awk -F" +|," '{for(i=4;i<=NF;i++){print $1,$2,$3,$i}}'  Input_file

在上面的代码中附加| column -t,以防您需要制表符分隔的输出。

根据 Cyrus 和 Ghoti 的评论,现在也添加以下内容,以防您的 Input_file 是 TAB 分隔的。

awk -F '[\t,]' -v OFS='\t' '{for(i=4; i<=NF; i++) print $1,$2,$3,$i}' Input_file

【讨论】:

  • 不错。不过,请注意输入文件是制表符分隔的,而不是空格。而不是通过column -t 进行管道传输,您可以按照我在回答中提到的那样设置您的OFS。
  • 例如awk -F '[\t,]' -v OFS='\t' '{for(i=4; i&lt;=NF; i++) print $1,$2,$3,$i}' file
  • @ghoti,感谢 Ghoti 让我度过了愉快的一天,Cyrus,当然现在也将它添加到代码中。
  • 天哪,你的编辑方法很聪明。做得很好! (它看起来好像与我的第二个解决方案相关联,因为它是缩短的 awk 代码以产生所需的结果。)
  • 经过反思,您可以通过删除不必要的空格再减少六个字符:'{gsub(",",ORS$1OFS$2OFS$3OFS,$4)} 1'。这肯定会让您在简洁性方面名列前茅,尽管它的可读性较差,并且可能不适用于所有版本的 awk。
【解决方案2】:

这应该可行:

awk '{n = split($4,x,","); for (i = 1; i <= n; ++i) {printf "%s %s %s %s\n", $1, $2, $3, x[i]} }' yourfile

【讨论】:

  • 有时我确实想知道为什么 awk 社区更喜欢单行。 gist.github.com/charles-dyfis-net/… 不是 - 相同的代码,但格式化后可以查看对齐情况以了解每个块和命令的开始和结束位置 - 更具可读性?
  • Shell 脚本允许在单引号字符串中使用文字换行符,因此仍然可以完美粘贴;只需要开头的awk ' 行和结尾的'
  • @CharlesDuffy 但是,在浏览您的本地历史记录时,单行代码不是更容易重用,例如通过另一个命令管道输出?
  • 在我的本地历史搜索中,向上箭头进入带有多行文字字符串的命令向我显示...具有多行字符串、空格和缩进的原始命令仍然完好无损,并且可用于编辑的跨行边界导航。
  • split 返回长度,只需在此处捕获并在 for 循环中使用。
【解决方案3】:

使用 GNU awk:

awk 'BEGIN{FS=OFS="\t"} {c1to3=$1 FS $2 FS $3; columns=split($4,array,","); for(i=1; i<=columns; i++) print c1to3,array[i]}' file

或更短:

awk -v OFS='\t' '{columns=split($4,array,","); for(i=1; i<=columns; i++) print $1,$2,$3,array[i]}' file

awk 'BEGIN{OFS="\t"} {c=split($4,a,","); NF=3; for(i=1; i<=c; i++) print $0,a[i]}' file

输出:

6338838 ESR 3 173812 6338838 ESR 3 10547556 6338838 ESR 3 10518181 6338822 ESR 2 7219086 6338822 ESR 2 12761162

【讨论】:

  • 这里面有 GNU 特有的东西吗?
  • @chepner:我只用 GNU awk 测试过。
【解决方案4】:

我喜欢这些“谁能做的更短”的比赛。 :-)

如果我们想使用 3 美元起的商品数量,我们可以这样做:

awk '{split($4,a,",");for(i=1;i<=$3;i++){$4=a[i];print}}' OFS='\t' input.txt

但以下代码会以更少的代码字节产生类似的结果。输出与 $4 中子字段的顺序相反。

awk '{for(i=split($4,a,",");i;i--){$4=a[i];print}}' OFS='\t' input.txt

无需设置FS,因为您的示例输入似乎没有在字段中包含空格。

【讨论】:

    【解决方案5】:

    在原生 bash 中:

    while IFS=$'\t' read -r one two three four; do
        IFS=, read -r -a pieces <<<"$four"
        for piece in "${pieces[@]}"; do
          printf '%s\t%s\t%s\t%s\n' "$one" "$two" "$three" "$piece"
        done
    done <yourfile
    

    【讨论】:

      【解决方案6】:

      这是另一个awk,没有引用未使用的字段。

      $ awk '{n=split($NF,a,","); 
              for(i=1;i<=n;i++) 
                {sub($NF"$",a[i]); 
                 print}}' file.t
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-06
        • 1970-01-01
        • 1970-01-01
        • 2015-10-10
        • 2011-08-23
        • 1970-01-01
        • 2019-03-04
        相关资源
        最近更新 更多