programing

Bash에서 파일을 효율적으로 변환하는 방법

powerit 2023. 4. 19. 00:32
반응형

Bash에서 파일을 효율적으로 변환하는 방법

이렇게 포맷된 큰 탭 구분 파일이 있습니다.

X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11

bash 명령어만을 사용하여 효율적으로 변환하고 싶다(Perl 스크립트를 10줄 정도 쓸 수 있지만 네이티브 bash 함수보다 실행 속도가 느릴 수 있다).따라서 출력은 다음과 같아야 합니다.

X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

나는 이런 해결책을 생각해 냈다.

cols=`head -n 1 input | wc -w`
for (( i=1; i <= $cols; i++))
do cut -f $i input | tr $'\n' $'\t' | sed -e "s/\t$/\n/g" >> output
done

하지만 속도가 느리고 가장 효율적인 솔루션은 아닌 것 같습니다.이 투고에서 vi의 솔루션을 확인했지만, 아직 너무 느립니다.아이디어/제안/명석한 아이디어가 있습니까? :-)

awk '
{ 
    for (i=1; i<=NF; i++)  {
        a[NR,i] = $i
    }
}
NF>p { p = NF }
END {    
    for(j=1; j<=p; j++) {
        str=a[1,j]
        for(i=2; i<=NR; i++){
            str=str" "a[i,j];
        }
        print str
    }
}' file

산출량

$ more file
0 1 2
3 4 5
6 7 8
9 10 11

$ ./shell.sh
0 3 6 9
1 4 7 10
2 5 8 11

Perl 솔루션에 대한 Jonathan의 퍼포먼스 (10000라인 파일)

$ head -5 file
1 0 1 2
2 3 4 5
3 6 7 8
4 9 10 11
1 0 1 2

$  wc -l < file
10000

$ time perl test.pl file >/dev/null

real    0m0.480s
user    0m0.442s
sys     0m0.026s

$ time awk -f test.awk file >/dev/null

real    0m0.382s
user    0m0.367s
sys     0m0.011s

$ time perl test.pl file >/dev/null

real    0m0.481s
user    0m0.431s
sys     0m0.022s

$ time awk -f test.awk file >/dev/null

real    0m0.390s
user    0m0.370s
sys     0m0.010s

Edit by Ed Morton (@ghostdog74가 동의하지 않으면 자유롭게 삭제 가능)

보다 명확한 변수 이름을 가진 이 버전은 아래 질문에 대한 답변과 스크립트의 동작을 명확하게 하는 데 도움이 될 수 있습니다.또한 OP가 원래 요청한 구분 기호로 탭을 사용하여 빈 필드를 처리하며, 공교롭게도 이 경우 출력이 약간 프리페이트업합니다.

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
    for (rowNr=1;rowNr<=NF;rowNr++) {
        cell[rowNr,NR] = $rowNr
    }
    maxRows = (NF > maxRows ? NF : maxRows)
    maxCols = NR
}
END {
    for (rowNr=1;rowNr<=maxRows;rowNr++) {
        for (colNr=1;colNr<=maxCols;colNr++) {
            printf "%s%s", cell[rowNr,colNr], (colNr < maxCols ? OFS : ORS)
        }
    }
}

$ awk -f tst.awk file
X       row1    row2    row3    row4
column1 0       3       6       9
column2 1       4       7       10
column3 2       5       8       11

위의 솔루션은 모든 awk에서 작동합니다(물론 YMMV는 오래된 Awk가 파손된 것은 제외).

위의 솔루션에서는 파일 전체를 메모리에 읽어 들일 수 있습니다.입력 파일이 너무 크면 다음과 같이 할 수 있습니다.

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ printf "%s%s", (FNR>1 ? OFS : ""), $ARGIND }
ENDFILE {
    print ""
    if (ARGIND < NF) {
        ARGV[ARGC] = FILENAME
        ARGC++
    }
}
$ awk -f tst.awk file
X       row1    row2    row3    row4
column1 0       3       6       9
column2 1       4       7       10
column3 2       5       8       11

메모리는 거의 사용하지 않지만 입력 파일을 한 줄의 필드 수당 한 번 읽기 때문에 파일 전체를 메모리로 읽는 버전보다 속도가 훨씬 느려집니다., 각 을 전제로 , 「」에 GNU 를 합니다.ENDFILE ★★★★★★★★★★★★★★★★★」ARGIND 어떤 도 같은 할 수 .FNR==1 ★★★★★★★★★★★★★★★★★」END.

아악

어레이 어레이를 사용하는 Gawk 버전:

tp(){ awk '{for(i=1;i<=NF;i++)a[i][NR]=$i}END{for(i in a)for(j in a[i])printf"%s"(j==NR?RS:FS),a[i][j]}' "${1+FS=$1}";}

다차원 어레이를 사용하는 일반 awk 버전(벤치마크에서는 약 2배 느림):

tp(){ awk '{for(i=1;i<=NF;i++)a[i,NR]=$i}END{for(i=1;i<=NF;i++)for(j=1;j<=NR;j++)printf"%s"(j==NR?RS:FS),a[i,j]}' "${1+FS=$1}";}

에는 Brian 버전(Brian Kingham)이 .nawk어레이 어레이를 지원하지 않습니다.

않고 하려면 을 합니다.FS='[ ]'.

rs

rs는 MacOS와 함께 제공되는 BSD 유틸리티이지만 다른 플랫폼의 패키지 매니저에서 사용할 수 있습니다.APL을 사용하다

공백 및 탭 시퀀스를 열 구분 기호로 사용합니다.

rs -T

탭을 열 구분 기호로 사용:

rs -c -C -T

열 구분 기호로 쉼표 사용:

rs -c, -C, -T

-c 열 기호와 입력 열 구분 기호를 합니다.-C을 사용하다 홀 a.-c ★★★★★★★★★★★★★★★★★」-C구분 기호를 탭으로 설정합니다. -T행과 열을 바꿉니다.

「 」를하지 주세요.-t-T이지만 "로할 수 ")가 -w를 참조해 주세요.

가 " " " 를 사용하여 -C되지만, 는 " " "로 할 수 . "로 할 수 있습니다.sed:

$ seq 4|paste -d, - -|rs -c, -C, -T
1,3,
2,4,
$ seq 4|paste -d, - -|rs -c, -C, -T|sed s/.\$//
1,3
2,4

rs -T는 첫 열첫의 빈열로 잘못된 를 생성합니다. 즉, " " " " " " " " " " " " " 입니다.

$ rs -c, -C, -T<<<$'1,\n3,4'
1,3,4,

R

t함수는 행렬 또는 데이터 프레임을 바꿉니다.

Rscript -e 'write.table(t(read.table("stdin",sep=",",quote="",comment.char="")),sep=",",quote=F,col.names=F,row.names=F)'

「」를 .Rscript -eR -e으로 STDOUT로되고 있는 「Da」 「STDOUT」도 ignoring SIGPIPE signal 에 R 명령어가 head -n1STDN을 읽기 전에 출발합니다.STDIN 전체를 읽기 전에 종료됩니다.

quote=""입력이 두 번 인용하거나 단일 인용되지 않은 경우, doesn 는 은 함 다 할 제 은 경 can 습우삭 니 수 않 quotes input있 the quotes orcomment.char=""입력이 해시 문자를 포함하지 않으면 제거할 수 있습니다.해시 문자로 시작하는 행이 입력에 포함되지 않은 경우 를 삭제할 수 있습니다.

For a big input file, 대용량 입력 파일의 경우,fread ★★★★★★★★★★★★★★★★★」fwrite부에서data.table are faster than 보다 빠르다read.table ★★★★★★★★★★★★★★★★★」write.table:

$ seq 1e6|awk 'ORS=NR%1e3?FS:RS'>a
$ time Rscript --no-init-file -e 'write.table(t(read.table("a")),quote=F,col.names=F,row.names=F)'>/dev/null
real  0m1.061s
user  0m0.983s
sys   0m0.074s
$ time Rscript --no-init-file -e 'write.table(t(data.table::fread("a")),quote=F,col.names=F,row.names=F)'>/dev/null

real  0m0.599s
user  0m0.535s
sys   0m0.048s
$ time Rscript --no-init-file -e 'data.table::fwrite(t(data.table::fread("a")),sep=" ",col.names=F)'>t/b
x being coerced from class: matrix to data.table

real  0m0.375s
user  0m0.296s
sys   0m0.073s

jq

tp(){ jq -R .|jq --arg x "${1-$'\t'}" -sr 'map(./$x)|transpose|map(join($x))[]';}

jq -R .는, 각 리터럴로서 합니다.-s )--slurp는 각 후또, 「 JSON」은 「JSON」으로 해석합니다.-r )--raw-output 리터럴이 는 JSON 문자열 리터럴이 아닌 문자열의 내용을 출력합니다./연산자가 문자열을 분할하기 위해 오버로드되었습니다.

루비

ruby -e'STDIN.map{|x|x.chomp.split(",",-1)}.transpose.each{|x|puts x*","}'

-1합니다.split에 빈 합니다.

$ ruby -e'p"a,,".split(",")'
["a"]
$ ruby -e'p"a,,".split(",",-1)'
["a", "", ""]

기능 양식:

$ tp(){ ruby -e's=ARGV[0];STDIN.map{|x|x.chomp.split(s==" "?/ /:s,-1)}.transpose.each{|x|puts x*s}' -- "${1-$'\t'}";}
$ seq 4|paste -d, - -|tp ,
1,3
2,4

는 " " " 를 사용합니다.s==" "?/ /:s '인수'에 split함수는 단일 공간이며 연속된 공간 및 탭에 따라 문자열이 분할되는 awk와 같은 특별한 동작을 가능하게 합니다.

$ ruby -e'p" a  \tb ".split(" ",-1)'
["a", "b", ""]
$ ruby -e'p" a  \tb ".split(/ /,-1)'
["", "a", "", "\tb", ""]

Python 솔루션:

python -c "import sys; print('\n'.join(' '.join(c) for c in zip(*(l.split() for l in sys.stdin.readlines() if l.strip()))))" < input > output

위의 내용은 다음 사항에 기초하고 있습니다.

import sys

for c in zip(*(l.split() for l in sys.stdin.readlines() if l.strip())):
    print(' '.join(c))

이 코드는 모든 행에 동일한 수의 열이 있다고 가정합니다(패딩은 수행되지 않음).

다음과 같이 사용할 수 있는 GNU datamash를 확인하십시오.datamash transpose 테이블 table도할 예정입니다.

공백으로 구분된 열을 사용하는 방법은 다음과 같습니다.

datamash transpose -t ' ' < file > transposed_file

소스 포지에 대한 전치 프로젝트는 정확히 그것을 위한 코어유틸과 같은 C 프로그램입니다.

gcc transpose.c -o transpose
./transpose -t input > output #works with stdin, too.

순수 BASH, 추가 프로세스 없음.좋은 연습:

declare -a array=( )                      # we build a 1-D-array

read -a line < "$1"                       # read the headline

COLS=${#line[@]}                          # save number of columns

index=0
while read -a line ; do
    for (( COUNTER=0; COUNTER<${#line[@]}; COUNTER++ )); do
        array[$index]=${line[$COUNTER]}
        ((index++))
    done
done < "$1"

for (( ROW = 0; ROW < COLS; ROW++ )); do
  for (( COUNTER = ROW; COUNTER < ${#array[@]}; COUNTER += COLS )); do
    printf "%s\t" ${array[$COUNTER]}
  done
  printf "\n" 
done

GNU datamash는 한 줄의 코드와 잠재적으로 큰 파일 크기를 가진 이 문제에 완벽하게 적합합니다.

datamash -W transpose infile > outfile

이를 위해 특별히 제작된 유틸리티가 있습니다.

GNU 데이터 캐시 유틸리티

apt install datamash  

datamash transpose < yourfile

이 사이트, http://www.thelinuxrain.com/articles/transposing-rows-and-columns-3-methods 및 http://www.thelinuxrain.com/articles/transposing-rows-and-columns-3-methods에서 가져온 정보입니다.

이 작업을 수행하기 위한 중간 정도의 견고한 Perl 스크립트를 다음에 나타냅니다.@@ghostdog74의 @ghostdog74의 @ghostdog74의 @ghostdog74의 @ghostdog74와 구조적 유사점이 있다.awk★★★★★★★★★★★★★★★★★★.

#!/bin/perl -w
#
# SO 1729824

use strict;

my(%data);          # main storage
my($maxcol) = 0;
my($rownum) = 0;
while (<>)
{
    my(@row) = split /\s+/;
    my($colnum) = 0;
    foreach my $val (@row)
    {
        $data{$rownum}{$colnum++} = $val;
    }
    $rownum++;
    $maxcol = $colnum if $colnum > $maxcol;
}

my $maxrow = $rownum;
for (my $col = 0; $col < $maxcol; $col++)
{
    for (my $row = 0; $row < $maxrow; $row++)
    {
        printf "%s%s", ($row == 0) ? "" : "\t",
                defined $data{$row}{$col} ? $data{$row}{$col} : "";
    }
    print "\n";
}

샘플 데이터 크기에서는 perl과 awk의 성능 차이는 무시할 수 있습니다(총 7개 중 1밀리초).더 큰 데이터 세트(100x100 매트릭스, 엔트리 각각6~8 문자)에서는 perl은 0.026s 대 0.042s의 awk를 약간 웃돌았습니다.어느 쪽도 문제가 되지 않을 것 같다.


MacOS X 10.5.8의 Perl 5.10.1(32비트) vs awk('-V'가 지정된 경우 버전 20040207) vs gawk 3.1.7(32비트)에 대한 대표적인 타이밍은 다음과 같습니다.

Osiris JL: time gawk -f tr.awk xxx  > /dev/null

real    0m0.367s
user    0m0.279s
sys 0m0.085s
Osiris JL: time perl -f transpose.pl xxx > /dev/null

real    0m0.138s
user    0m0.128s
sys 0m0.008s
Osiris JL: time awk -f tr.awk xxx  > /dev/null

real    0m1.891s
user    0m0.924s
sys 0m0.961s
Osiris-2 JL: 

gawk는 이 머신의 awk보다 훨씬 빠르지만 perl보다는 여전히 느립니다.물론 주행거리는 다양합니다.

모든 행의 필드 수가 같다고 가정하면 이 awk 프로그램은 문제를 해결합니다.

{for (f=1;f<=NF;f++) col[f] = col[f]":"$f} END {for (f=1;f<=NF;f++) print col[f]}

각 각 루프를 합니다.fcol[f]해당 필드의 요소를 포함합니다.모든 행을 완료한 후 각 문자열을 별도의 행으로 인쇄합니다.':'로 바꾸면 . 로 할 때 ':'로합니다.tr ':' ' '.

예:

$ echo "1 2 3\n4 5 6"
1 2 3
4 5 6

$ echo "1 2 3\n4 5 6" | awk '{for (f=1;f<=NF;f++) col[f] = col[f]":"$f} END {for (f=1;f<=NF;f++) print col[f]}' | tr ':' ' '
 1 4
 2 5
 3 6

인스톨 되어 있는 경우는, 다음의 조작을 실행할 수 있습니다.

psc -r < inputfile | sc -W% - > outputfile

는 보통 이 i i i i i i i i i i 。awk: "snippet" :

  awk '{for (i=1; i<=NF; i++) a[i,NR]=$i
        max=(max<NF?NF:max)}
        END {for (i=1; i<=max; i++)
              {for (j=1; j<=NR; j++) 
                  printf "%s%s", a[i,j], (j==NR?RS:FS)
              }
        }' file

됩니다.a[line,column] 해서 다시 를 합니다.a[column,line]지정된 입력이 이동하도록 합니다.

는 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아max첫 번째 파일에 있는 열의 imum 양으로, 인쇄하는 행의 수로 사용됩니다.

해키쉬한 perl 솔루션은 다음과 같습니다.모든 파일을 메모리에 로드하지 않고 중간 임시 파일을 인쇄한 다음 모든 놀라운 붙여넣기를 사용할 수 있기 때문에 좋습니다.

#!/usr/bin/perl
use warnings;
use strict;

my $counter;
open INPUT, "<$ARGV[0]" or die ("Unable to open input file!");
while (my $line = <INPUT>) {
    chomp $line;
    my @array = split ("\t",$line);
    open OUTPUT, ">temp$." or die ("unable to open output file!");
    print OUTPUT join ("\n",@array);
    close OUTPUT;
    $counter=$.;
}
close INPUT;

# paste files together
my $execute = "paste ";
foreach (1..$counter) {
    $execute.="temp$counter ";
}
$execute.="> $ARGV[1]";
system $execute;

고객님의 예에서 유일하게 개선되는 점은 awk를 사용하는 것입니다.이것에 의해, 실행되는 프로세스의 수와 그 사이에 파이핑 되는 데이터의 양이 삭감됩니다.

/bin/rm output 2> /dev/null

cols=`head -n 1 input | wc -w` 
for (( i=1; i <= $cols; i++))
do
  awk '{printf ("%s%s", tab, $'$i'); tab="\t"} END {print ""}' input
done >> output

일부 *nix 표준 util 1라인. 임시 파일은 필요하지 않습니다.NB: OP는 효율적인 수정(즉, 빠른 수정)을 원했습니다.상위 답변은 보통 이 답변보다 빠릅니다.이 원라인은 *nix 소프트웨어 툴을 좋아하는 분들을 위한 입니다.드문 경우(예: 입출력 및 메모리 부족) 이러한 스니펫은 실제로 상위 답변보다 빠를 수 있습니다.

입력 파일 foo를 호출합니다.

  1. foo에 4개의 열이 있는 경우:

    for f in 1 2 3 4 ; do cut -d ' ' -f $f foo | xargs echo ; done
    
  2. foo의 열 수를 모르는 경우:

    n=$(head -n 1 foo | wc -w)
    for f in $(seq 1 $n) ; do cut -d ' ' -f $f foo | xargs echo ; done
    

    xargs에는 사이즈 제한이 있기 때문에 긴 파일에서는 불완전하게 동작합니다.시스템에 따라 달라지는 크기 제한은 다음과 같습니다.

    { timeout '.01' xargs --show-limits ; } 2>&1 | grep Max
    

    실제로 사용할 수 있는 최대 명령어 길이: 208894

  3. tr&echo:

    for f in 1 2 3 4; do cut -d ' ' -f $f foo | tr '\n\ ' ' ; echo; done
    

    ...또는 열의 수를 알 수 없는 경우:

    n=$(head -n 1 foo | wc -w)
    for f in $(seq 1 $n); do 
        cut -d ' ' -f $f foo | tr '\n' ' ' ; echo
    done
    
  4. 「」를 사용합니다.set를 들면, 「」와 같이 됩니다.xargs에는 다음과 같은 명령줄 크기 기반 제한이 있습니다.

    for f in 1 2 3 4 ; do set - $(cut -d ' ' -f $f foo) ; echo $@ ; done
    

fgm의 솔루션(고마워 fgm!)을 사용했지만 각 행의 끝에 있는 탭 문자를 삭제해야 했기 때문에 스크립트를 다음과 같이 수정했습니다.

#!/bin/bash 
declare -a array=( )                      # we build a 1-D-array

read -a line < "$1"                       # read the headline

COLS=${#line[@]}                          # save number of columns

index=0
while read -a line; do
    for (( COUNTER=0; COUNTER<${#line[@]}; COUNTER++ )); do
        array[$index]=${line[$COUNTER]}
        ((index++))
    done
done < "$1"

for (( ROW = 0; ROW < COLS; ROW++ )); do
  for (( COUNTER = ROW; COUNTER < ${#array[@]}; COUNTER += COLS )); do
    printf "%s" ${array[$COUNTER]}
    if [ $COUNTER -lt $(( ${#array[@]} - $COLS )) ]
    then
        printf "\t"
    fi
  done
  printf "\n" 
done

나는 단지 비슷한 bash tranpose를 찾고 있었지만 패딩에 대한 지원이 있었다.여기 fgm의 솔루션을 바탕으로 작성한 스크립트가 효과가 있을 것 같습니다.도움이 된다면...

#!/bin/bash 
declare -a array=( )                      # we build a 1-D-array
declare -a ncols=( )                      # we build a 1-D-array containing number of elements of each row

SEPARATOR="\t";
PADDING="";
MAXROWS=0;
index=0
indexCol=0
while read -a line; do
    ncols[$indexCol]=${#line[@]};
((indexCol++))
if [ ${#line[@]} -gt ${MAXROWS} ]
    then
         MAXROWS=${#line[@]}
    fi    
    for (( COUNTER=0; COUNTER<${#line[@]}; COUNTER++ )); do
        array[$index]=${line[$COUNTER]}
        ((index++))

    done
done < "$1"

for (( ROW = 0; ROW < MAXROWS; ROW++ )); do
  COUNTER=$ROW;
  for (( indexCol=0; indexCol < ${#ncols[@]}; indexCol++ )); do
if [ $ROW -ge ${ncols[indexCol]} ]
    then
      printf $PADDING
    else
  printf "%s" ${array[$COUNTER]}
fi
if [ $((indexCol+1)) -lt ${#ncols[@]} ]
then
  printf $SEPARATOR
    fi
    COUNTER=$(( COUNTER + ncols[indexCol] ))
  done
  printf "\n" 
done

모든 종류의 매트릭스(nxn 또는 mxn)를 모든 종류의 데이터(숫자 또는 데이터)와 치환할 수 있는 솔루션을 찾고 있었는데 다음과 같은 솔루션을 얻었습니다.

Row2Trans=number1
Col2Trans=number2

for ((i=1; $i <= Line2Trans; i++));do
    for ((j=1; $j <=Col2Trans ; j++));do
        awk -v var1="$i" -v var2="$j" 'BEGIN { FS = "," }  ; NR==var1 {print $((var2)) }' $ARCHIVO >> Column_$i
    done
done

paste -d',' `ls -mv Column_* | sed 's/,//g'` >> $ARCHIVO

파일에서 $N 행만 가져와 열로 변환하는 경우:

head -$N file | tail -1 | tr ',' '\n'

그다지 우아하지는 않지만, 이 "단일 행" 명령어는 문제를 빠르게 해결합니다.

cols=4; for((i=1;i<=$cols;i++)); do \
            awk '{print $'$i'}' input | tr '\n' ' '; echo; \
        done

, 를 4로 대체할 수 .head -n 1 input | wc -w.

하나awk메모리 용량에 따라 입력이 제한되어 있습니다.

awk '{ for (i=1; i<=NF; i++) RtoC[i]= (RtoC[i]? RtoC[i] FS $i: $i) }
    END{ for (i in RtoC) print RtoC[i] }' infile

, 같은 해, 「」로 합니다.END은 첫 번째 행, 두은 두 번째 행등 합니다.언언: :

X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
#!/bin/bash

aline="$(head -n 1 file.txt)"
set -- $aline
colNum=$#

#set -x
while read line; do
  set -- $line
  for i in $(seq $colNum); do
    eval col$i="\"\$col$i \$$i\""
  done
done < file.txt

for i in $(seq $colNum); do
  eval echo \${col$i}
done

set eval

에서는 각 하고 "Bash"를 "Bash"로 한 Bash 줍니다.각 행을 컬럼으로 변환하고paste -이것들.

echo '' > tmp1;  \
cat m.txt | while read l ; \
            do    paste tmp1 <(echo $l | tr -s ' ' \\n) > tmp2; \
                  cp tmp2 tmp1; \
            done; \
cat tmp1

m.txt:

0 1 2
4 5 6
7 8 9
10 11 12
  1. 작성하다tmp1파일이 비어있지 않습니다.

  2. 행을 합니다.tr

  3. 을 붙입니다.tmp1

  4. tmp1.

PS: io-descriptor를 꼭 사용하고 싶었지만 작동하지 않았습니다.

다른 bash 배리언트

$ cat file 
XXXX    col1    col2    col3
row1    0       1       2
row2    3       4       5
row3    6       7       8
row4    9       10      11

대본

#!/bin/bash

I=0
while read line; do
    i=0
    for item in $line; { printf -v A$I[$i] $item; ((i++)); }
    ((I++))
done < file
indexes=$(seq 0 $i)

for i in $indexes; {
    J=0
    while ((J<I)); do
        arr="A$J[$i]"
        printf "${!arr}\t"
        ((J++))
    done
    echo
}

산출량

$ ./test 
XXXX    row1    row2    row3    row4    
col1    0       3       6       9   
col2    1       4       7       10  
col3    2       5       8       11

게임에 조금 늦었지만 이건 어때?

cat table.tsv | python -c "import pandas as pd, sys; pd.read_csv(sys.stdin, sep='\t').T.to_csv(sys.stdout, sep='\t')"

★★★★★★★★★★★★★★★★★」zcat퍼퍼면면면면면면면

있다고 하는 것입니다.pandas의 「」에 되어 있습니다.python

여기 아주 간단한 루비가 있습니다.

ruby -lane 'BEGIN{lines=[]} 
    lines<<$F 
END{lines.transpose.each{|row| puts row.join("\t")}}' file

, 「 」, 「 」, 「 」, 「 」의 커맨드 라인 하지 않고, 분할을 할 수 .-lan:

ruby -e '$<.read.
            split(/\R+/).
            map(&:split).
            transpose.each{|row| puts row.join("\t")}' file

또는 Ruby의 CSV 모듈을 사용할 수 있습니다.

ruby -r csv -e '
tbl=CSV.parse($<.read, **{:headers=>false, :col_sep=>" "}) # or use :col_sep=>"\t" for tab columns 
tbl.transpose.each{|row| puts row.join("\t")}
' file 

인쇄물:

X   row1    row2    row3    row4
column1 0   3   6   9
column2 1   4   7   10
column3 2   5   8   11

여기 Haskell 솔루션이 있습니다.-O2로 컴파일하면 ghostdog's awk보다 약간 빠르고 Stephan의 얇은 c python보다 약간 느립니다.유감스럽게도 GHC의 명령줄 코드 전달 지원은 제가 알기로는 존재하지 않기 때문에 직접 파일에 작성해야 합니다.그러면 행이 가장 짧은 행의 길이로 잘립니다.

transpose :: [[a]] -> [[a]]
transpose = foldr (zipWith (:)) (repeat [])

main :: IO ()
main = interact $ unlines . map unwords . transpose . map words . lines

어레이 전체를 메모리에 저장하는 awk 솔루션

    awk '$0!~/^$/{    i++;
                  split($0,arr,FS);
                  for (j in arr) {
                      out[i,j]=arr[j];
                      if (maxr<j){ maxr=j}     # max number of output rows.
                  }
            }
    END {
        maxc=i                 # max number of output columns.
        for     (j=1; j<=maxr; j++) {
            for (i=1; i<=maxc; i++) {
                printf( "%s:", out[i,j])
            }
            printf( "%s\n","" )
        }
    }' infile

그러나 출력 행이 필요한 횟수만큼 파일을 "워크"할 수 있습니다.

#!/bin/bash
maxf="$(awk '{if (mf<NF); mf=NF}; END{print mf}' infile)"
rowcount=maxf
for (( i=1; i<=rowcount; i++ )); do
    awk -v i="$i" -F " " '{printf("%s\t ", $i)}' infile
    echo
done

어느 쪽(출력 행의 카운트가 작을 경우 이전 코드보다 빠릅니다).

R을 사용하는 오네라이너...

  cat file | Rscript -e "d <- read.table(file('stdin'), sep=' ', row.names=1, header=T); write.table(t(d), file=stdout(), quote=F, col.names=NA) "

이하의 2개의 스크립트를 사용해 같은 조작을 실시한 적이 있습니다.첫 번째는 awk로 "순수한" 배시의 두 번째보다 훨씬 더 빠릅니다.자신의 어플리케이션에 맞게 조정할 수 있을지도 모릅니다.

awk '
{
    for (i = 1; i <= NF; i++) {
        s[i] = s[i]?s[i] FS $i:$i
    }
}
END {
    for (i in s) {
        print s[i]
    }
}' file.txt
declare -a arr

while IFS= read -r line
do
    i=0
    for word in $line
    do
        [[ ${arr[$i]} ]] && arr[$i]="${arr[$i]} $word" || arr[$i]=$word
        ((i++))
    done
done < file.txt

for ((i=0; i < ${#arr[@]}; i++))
do
    echo ${arr[i]}
done

언급URL : https://stackoverflow.com/questions/1729824/an-efficient-way-to-transpose-a-file-in-bash

반응형