使用 UNIX 命令将字符串排序为新行和单引号

使用 UNIX 命令将字符串排序为新行和单引号

我有一堆文本,看起来像这样:

K1      CM1     TN1     CT14    D01
K2      CM2     TN2     CT15    D02
K3      CM3     TN3     CT16    D03
K4      CM4     TN4     CT17    D04
K5      CM5     TN5     CT18    D05
K6      CM6     TN6     CT19    D06
K7      CM11    TN7     CT20    D07
K8      CM12    TN8             D08
TW10    CM15    TN9             D09
TW11            TN10            D11
TW12            TN11            D12
TW16            TN12            D6W

(列可以为空,并由一个制表符分隔,而不是空格 - cas)

我想使用 BBedit 中的 Run Unix 命令功能将此文本转换为以下内容:

'K1',
'K2',
'K3',
'K4',
'K5',
'K6',
'K7',
'K8',
'TW10',
'TW11',
'TW12',
'TW16',
'CM1',
'CM2',
'CM3',
'CM4',
'CM5',
'CM6',
'CM11',
'CM12',
'CM15',
'TN1',
'TN2',
'TN3',
'TN4',
'TN5',
'TN6',
'TN7',
'TN8',
'TN9',
'TN10',
'TN11',
'TN12',
'CT14',
'CT15',
'CT16',
'CT17',
'CT18',
'CT19',
'CT20',
'D01',
'D02',
'D03',
'D04',
'D05',
'D06',
'D07',
'D08',
'D09',
'D11',
'D12',
'D6W'

正如您所看到的,每个字符串都用单引号引起来,并与最终字符串分开,后跟一个逗号。

提前致谢。

答案1

假设输入包含具有制表符分隔字段的行,并且您首先需要每行从上到下的第一个字段,然后是第二个字段等,再加上引号和逗号,这里有一些廉价的解决方案。

丑陋的两行最多 9 列(使用 sed 进行格式化):

for i in 1 2 3 4 5 6 7 8 9; do cut -f$i file.txt; done | 
    grep -v '^$' | sed  -e "s/^/'/" -e "s/\$/'/" -e '$!s/$/,/'

使用 AWK 手动转置(加上 sed 删除最后一个逗号):

awk -F'\t' 'NF > cols {cols=NF}
                      {for (i=1; i<=NF; i++) { a[i,NR]=$i }} 
            END { for (j=1;j<=cols;j++) for (i=1;i<=NR;i++)
                      if (a[j, i] != "") printf "\047%s\047,\n", a[j, i] }' file.txt |
    sed -e '$s/,$//'

使用 GNU datamash(以及用于格式化的 sed):

datamash --no-strict transpose < file.txt | tr -s '\t' '\n' |
    sed -e "s/^/'/" -e "s/\$/'/" -e '$!s/$/,/'

(在 Linux 上测试,前两个也适用于 macOS)

答案2

您可能想尝试使用 grep 和 sed 的方法:

for i in K TW CM TN CT D ;do grep -ow $i[0-9]*[A-Z]* <input_file;done

如果您想将每个项目放在单引号中并在每行末尾添加逗号,则将上面的内容通过管道传输到此命令:

sed "s/$/\'\,/" | sed "s/^/\'/"

答案3

我不确定这the Run Unix command feature in BBedit意味着什么,但如果您只是寻找一个 Unix 命令来根据您显示的输入生成您显示的输出,那么 - 使用任何 awk 并假设您的字段由制表符分隔:

$ cat tst.awk
BEGIN { FS="\t" }
{
    for ( i=1; i<=NF; i++ ) {
        if ( $i != "" ) {
            a[i,NR] = $i
            max_j[i] = NR
        }
    }
    max_i = (max_i > NF ? max_i : NF)
}
END {
    for ( i=1; i<=max_i; i++ ) {
        for ( j=1; j<=max_j[i]; j++ ) {
            printf "%s\047%s\047", sep, a[i,j]
            sep = ",\n"
        }
    }
    print ""
}

$ awk -f tst.awk file
'K1',
'K2',
'K3',
'K4',
'K5',
'K6',
'K7',
'K8',
'TW10',
'TW11',
'TW12',
'TW16',
'CM1',
'CM2',
'CM3',
'CM4',
'CM5',
'CM6',
'CM11',
'CM12',
'CM15',
'TN1',
'TN2',
'TN3',
'TN4',
'TN5',
'TN6',
'TN7',
'TN8',
'TN9',
'TN10',
'TN11',
'TN12',
'CT14',
'CT15',
'CT16',
'CT17',
'CT18',
'CT19',
'CT20',
'D01',
'D02',
'D03',
'D04',
'D05',
'D06',
'D07',
'D08',
'D09',
'D11',
'D12',
'D6W'

答案4

使用perl:

$ perl -lne '
    # split each input line on single tabs (not one-or-more
    # whitespace chars) into temporary array @F.
    @F = split /\t/;

    # Construct an Array-of-Arrays (AoA) containing the data.
    # This works with any number of columns, not limited to 5.
    # Assumes each input line has the same number of fields,
    # although some may be empty.
    foreach my $i (keys @F) {
        if (length($F[$i]) > 0) {
          push @{$columns[$i]}, "\047" . $F[$i] ."\047" ;
        };
    };

    END {
        # flatten the AoA with map and print it
        print join(",\n", map { @$_ } @columns);
    }' input.txt 
'K1',
'K2',
'K3',
'K4',
'K5',
'K6',
'K7',
'K8',
'TW10',
'TW11',
'TW12',
'TW16',
'CM1',
'CM2',
'CM3',
'CM4',
'CM5',
'CM6',
'CM11',
'CM12',
'CM15',
'TN1',
'TN2',
'TN3',
'TN4',
'TN5',
'TN6',
'TN7',
'TN8',
'TN9',
'TN10',
'TN11',
'TN12',
'CT14',
'CT15',
'CT16',
'CT17',
'CT18',
'CT19',
'CT20',
'D01',
'D02',
'D03',
'D04',
'D05',
'D06',
'D07',
'D08',
'D09',
'D11',
'D12',
'D6W'

@columns这会逐行读取输入,使用单个制表符作为分隔符分割每一行,并构造一个从每行的非空列调用的数组数组,这些列由单引号(八进制\047)包围。

当整个文件被读取后,它会使用map“展平”(即转换为单个数组)并在,\n每个元素之间打印它。


BTW,map是一个内置的 perl 函数,用于对列表/数组执行操作。它可以被认为是一个奇特的专门for循环。perldoc -f map详情请参阅。perldoc -f split如果您不熟悉 perl 的split函数,阅读也会很有用。还有perldoc -f join

相关内容