csv 根据不同的分隔符重新排列列顺序

xghobddn  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(106)

我写了一个Bash脚本,它接收输入文件,输出文件,定界符和列名,以便允许用户通过列名输入所需的列排列,并在输出文件中获得该排列。它还允许用户通过为discard参数写入yes或no来选择是否丢弃其余的列(参见下面的示例)。
示例输入文件:

Name    Age Pets
Alice   32  Fluffy
Bob 45  Whiskers
Charlie 27  Buddy

字符串
用户命令示例:

bash arrange.sh  pets_rearranged.tsv no pets.tsv Age Name


输出示例:

Age Name        Pets
32  Alice       Fluffy
45  Bob     Whiskers
27  Charlie     Buddy


我的剧本:

#!/bin/bash

name=$1
discard=$2
input=$3

cmd=("$@")

my_list=()

for (( i=3; i<${#cmd[@]}; i++ )); do
    my_list+=( $(awk -v RS='\t' 'tolower($0) ~ tolower("'"${cmd[$i]}"'") {print NR; exit}' $input) );
done

list=$(echo "${my_list[@]}")

awk -vd="$list" 'BEGIN{split(d, a, " ")} {for (i in a) printf "%s\t", $(a[i]); printf "\n"}' $input > first.txt

if [ $2 == 'no' ]
then
  cut -f"${list}" --complement $input > last.txt
else
  touch last.txt
fi

paste first.txt last.txt > $name


该脚本可以完美地与TSV文件一起工作,但我想让它也适用于其他类型的文件,例如逗号或哈希,但是当我在脚本中更改文件时,它不再工作,我不明白为什么:

#!/bin/bash
    
    name=$1
    discard=$2
    input=$3
    
    cmd=("$@")
    
    my_list=()
    
    for (( i=3; i<${#cmd[@]}; i++ )); do
        my_list+=( $(awk -v RS='#' 'tolower($0) ~ tolower("'"${cmd[$i]}"'") {print NR; exit}' $input) );
    done
    
    list=$(echo "${my_list[@]}")
    
    awk -vd="$list" 'BEGIN{split(d, a, " ")} {for (i in a) printf "%s#", $(a[i]); printf "\n"}' $input > first.txt
    
    if [ $2 == 'no' ]
    then
      cut -f"${list}" --complement -d'#' $input > last.txt
    else
      touch last.txt
    fi
    
    paste first.txt last.txt > $name


在这种情况下,对于输入:

CHR#NUM1#NUM2
chr22#9999999#10001000
chr22#10009999#10010100

我得到输出:

#CHR#NUM1#NUM2#
#chr22#9999999#10001000#
#chr22#10009999#10010100#


对于命令:

bash arrange.sh  hash_rearranged.tsv no hash.tsv NUM1 CHR


我想实现的脚本工作与不同的分隔符。

mfuanj7w

mfuanj7w1#

使用任何awk:

$ cat arrange.sh
#!/usr/bin/env bash

outfile=$1
discard=$2
infile=$3
shift 3

inFldSep="${fldSep:-\t}"
outFldSep="${outFldSep:-$inFldSep}"

awk -F"$inFldSep" -v OFS="$outFldSep" -v discard="$discard" -v tgts="$*" '
    NR == 1 {
        numOutFlds = split(tgts,outNrs2tags," ")
        for ( outFldNr=1; outFldNr<=numOutFlds; outFldNr++ ) {
            tag = outNrs2tags[outFldNr]
            tags2outNrs[tag] = outFldNr
        }

        for ( inFldNr=1; inFldNr<=NF; inFldNr++ ) {
            tag = $inFldNr
            tags2inNrs[tag] = inFldNr
            if ( discard == "no" ) {
                if ( !(tag in tags2outNrs) ) {
                    outNrs2tags[++numOutFlds] = tag
                }
            }
        }

        for ( outFldNr=1; outFldNr<=numOutFlds; outFldNr++ ) {
            tag = outNrs2tags[outFldNr]
            inFldNr = tags2inNrs[tag]
            out2in[outFldNr] = inFldNr
        }
    }
    {
        for ( outFldNr=1; outFldNr<=numOutFlds; outFldNr++ ) {
            inFldNr = out2in[outFldNr]
            printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
        }
    }
' "$infile" > "$outfile"

字符串

$ ./arrange.sh pets_rearranged.tsv no pets.tsv Age Name

$ cat pets_rearranged.tsv
Age     Name    Pets
32      Alice   Fluffy
45      Bob     Whiskers
27      Charlie Buddy

$ fldSep='#' ./arrange.sh hash_rearranged.tsv no hash.tsv NUM1 CHR

$ cat hash_rearranged.tsv
NUM1#CHR#NUM2
9999999#chr22#10001000
10009999#chr22#10010100

$ fldSep='#' ./arrange.sh hash_rearranged.tsv yes hash.tsv NUM1 CHR
$ cat hash_rearranged.tsv
NUM1#CHR
9999999#chr22
10009999#chr22


上面的代码可以做的比你当前的脚本做的更多,包括转换字段分隔符:

$ cat hash.tsv
CHR#NUM1#NUM2
chr22#9999999#10001000
chr22#10009999#10010100

$ fldSep='#' outFldSep='\t' ./arrange.sh hash_rearranged.tsv no hash.tsv
$ cat hash_rearranged.tsv
CHR     NUM1    NUM2
chr22   9999999 10001000
chr22   10009999        10010100

并且做得更健壮、更可移植、更高效,但是如果你想能够处理所有的字符分隔文件,这些文件遵循与CSV类似的规则,包括支持分隔符和出现在引号内的换行符,那么它还需要做更多的工作。有关详细信息,请参阅What's the most robust way to efficiently parse CSV using awk?
我不会选择上面的接口,在那里你必须设置脚本外部的变量,这些变量在脚本内部是已知的,以改变输入/输出字段分隔符,我会使用POSIX标准-argument [option] API和getopts循环来解析它们,但我不想改变你当前的API,如果你喜欢的话,你可以这样做。

ujv3wf0j

ujv3wf0j2#

CSV和TSV文件普遍接受的格式包括引用规则,你真的不想在awk或bash中尝试。有很多工具可以处理CSV和TSV文件,这里有一个小的选择:
https://github.com/secretGeek/AwesomeCSV
以下是CSV文件的普遍接受的定义:
https://datatracker.ietf.org/doc/html/rfc4180

相关问题