Bash에서 파일을 바꾸는 효율적인 방법


110

이렇게 형식이 지정된 탭으로 구분 된 거대한 파일이 있습니다.

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

bash 명령 만 사용하여 효율적인 방법 으로 전치 하고 싶습니다 (10 줄 정도의 Perl 스크립트를 작성할 수 있지만 기본 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에 대한 솔루션을 보았지만 여전히 너무 느립니다. 생각 / 제안 / 훌륭한 아이디어가 있습니까? :-)


12
Perl 스크립트보다 더 빠른 bash 스크립트가 존재할 것이라고 생각하는 이유는 무엇입니까? 이것이 바로 Perl이 뛰어난 문제입니다.
Mark Pim

1
@mark, 순수한 bash 경우 모든 cut / sed 등 도구를 함께 연결하는 것보다 빠를 수 있습니다. 그러나 다시 말하지만, 결합 도구에서와 같이 "bash"를 정의하면 awk 스크립트를 작성하는 것만으로도 Perl wrt 텍스트 처리와 비슷할 것입니다.
ghostdog74

여기에서 perl이 얼마나 느린 지 이해하지 못하도록 다른 것을 추가하십시오. 코드 작성이 느립니까? 실행 속도가 느립니까? 나는 진심으로 펄을 싫어하지만 이런 종류의 작업에서 탁월하다.
Corey Porter

열 / 필드의 크기 / 너비가 고정 된 경우 Python 파일 탐색을 사용하여 파일을 메모리로 읽는 것을 방지 할 수 있습니다. 고정 된 열 / 필드 크기 / 너비가 있습니까?
tommy.carstensen 2013-04-07

2
쉘 스크립트가 awk 또는 perl보다 빠를 것이라고 생각하는 사람은 unix.stackexchange.com/questions/169716/… 을 읽어야 합니다. 그래야 왜 그렇지 않은지 이해할 수 있습니다.
Ed Morton

답변:


114
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

10000 라인 파일에서 Jonathan의 Perl 솔루션에 대한 성능

$ 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

편집 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에서 작동합니다 (물론 오래된 깨진 awk 제외-YMMV).

위의 솔루션은 전체 파일을 메모리로 읽습니다. 입력 파일이 너무 크면 다음을 수행 할 수 있습니다.

$ 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 AWK를 사용하는 가정 ENDFILE하고 ARGIND있지만 AWK가 테스트와 동일한 기능을 수행 할 수 있습니다 FNR==1END.


이제 행 및 열 레이블도 처리해야합니까?
Jonathan Leffler

네-맞습니다. 샘플 데이터가 질문의 샘플 데이터와 일치하지 않지만 코드가 질문의 샘플 데이터에서 잘 작동하고 필요한 출력을 제공합니다 (빈칸 또는 탭 간격 사용). 주로 내 실수입니다.
Jonathan Leffler

흥미로운 타이밍-awk에서 성능상의 이점을 보는 데 동의합니다. 저는 'gawk'를 사용하지 않는 MacOS X 10.5.8을 사용하고있었습니다. Perl 5.10.1 (32 비트 빌드)을 사용하고있었습니다. 나는 당신의 데이터가 한 줄에 4 열이있는 10000 줄이라고 수집합니까? 어쨌든, 그것은별로 중요하지 않습니다. awk와 perl은 모두 실행 가능한 솔루션입니다 (그리고 awk 솔루션은 더 깔끔합니다. Perl의 '정의 된'검사는 엄격한 / 경고 하에서 경고가없는 실행을 경고하는 데 필요합니다). 둘 다 느슨하지 않으며 둘 다 원본보다 빠를 가능성이 높습니다. 쉘 스크립트 솔루션.
Jonathan Leffler

원래 2.2GB 매트릭스에서 perl 솔루션은 awk보다 약간 빠릅니다-350.103s 대 369.410s 저는 perl 5.8.8 64 비트를 사용했습니다
Federico Giorgi

1
@ zx8754에서 최대 필드 수는 POSIX가 아닌 오래된 awk에만 적용됩니다. 아마도 믿을 수 없을만큼 불행하게도 "nawk"라는 이름의 이름 일 것입니다. gawk 또는 기타 현대 awks에는 적용되지 않습니다.
Ed Morton

47

또 다른 옵션은 다음을 사용하는 것입니다 rs.

rs -c' ' -C' ' -T

-c입력 열 구분 기호를 -C변경하고, 출력 열 구분 기호를 변경하고, -T행과 열을 전치합니다. -t대신 사용하지 마십시오 -T. 일반적으로 정확하지 않은 자동 계산 된 행 및 열 수를 사용하기 때문입니다.rsAPL의 reshape 함수 이름을 따서 명명 된은 BSD 및 OS X와 ​​함께 제공되지만 다른 플랫폼의 패키지 관리자에서 사용할 수 있어야합니다.

두 번째 옵션은 Ruby를 사용하는 것입니다.

ruby -e'puts readlines.map(&:split).transpose.map{|x|x*" "}'

세 번째 옵션은 다음을 사용하는 것입니다 jq.

jq -R .|jq -sr 'map(./" ")|transpose|map(join(" "))[]'

jq -R .하는 JSON 문자열 리터럴 각 입력 라인 출력 -s( --slurp) JSON 각 라인을 파싱 한 후, 입력 라인 어레이를 생성하고, -r(가 --raw-output) 대신 JSON 문자열 리터럴 문자열의 내용을 출력한다. /연산자 분할 문자열 과부하.


3
저는 익숙하지 않았습니다 rs. 포인터 주셔서 감사합니다! (링크는 데비안으로, 업스트림은 mirbsd.org/MirOS/dist/mir/rs보입니다 )
tripleee

2
@lalebarde 적어도 rsOS X와 ​​함께 제공 되는 구현 -c에서는 입력 열 구분 기호를 탭으로 설정합니다.
nisetama

2
@lalebarde, 시도 강타의 인용 ANSI-C 탭 문자를 얻을 수는 :$'\t'
글렌 잭맨

3
이것은 극단적 인 경우이지만 TTC TTA TTC TTC TTT, 같은 행이 많은 매우 큰 파일의 경우 실행 rs -c' ' -C' ' -T < rows.seq > cols.seq하면 rs: no memory: Cannot allocate memory. 이것은 32GB의 램으로 FreeBSD 11.0-RELEASE를 실행하는 시스템입니다. 그래서 제 생각에는 rs모든 것을 RAM에 넣습니다. 속도에는 좋지만 대용량 데이터에는 적합하지 않습니다.
JRM

1
jq는 766MB 파일에서 21Gb의 램을 사용했습니다. 나는 출력없이 40 분 후에 그것을 죽였다.
Glubbdrubb

30

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))

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


3
한 가지 사소한 문제 : Replace l.split()by l.strip().split()(Python 2.7), 그렇지 않으면 출력의 마지막 줄이 손상됩니다. 임의의 열 구분 기호에 대해 작동하며 구분 기호가 variable에 저장된 경우 l.strip().split(sep)sep.join(c)사용하십시오 sep.
krlmlr

21

sourceforge 의 전치 프로젝트는 정확히이를위한 coreutil과 유사한 C 프로그램입니다.

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

링크 주셔서 감사합니다. 그러나 큰 행렬 / 파일을 처리 할 때 너무 많은 메모리가 필요합니다.
tommy.carstensen

blocksize 및 fieldsize에 대한 인수가 있습니다. -b-f인수를 조정 해보십시오 .
플라잉 양

기본 블록 크기 (--block 또는 -b)는 10kb이고 기본 필드 크기 (--fieldmax 또는 -f)는 64이므로 그럴 수 없습니다. 나는 시도했다. 그래도 제안 해 주셔서 감사합니다.
tommy.carstensen

1
2GB 크기의 csv에서 잘 작동했습니다.
discipulus

2
크기가 약 11k x 5k 인 매트릭스 파일의 경우 transpose.c가 ghostdog74의 첫 번째 awk 솔루션보다 ~ 7 배 더 빠르고 ~ 5 배 더 많은 메모리 효율성을 발견했습니다. 또한 ghostdog74의 "거의 메모리를 사용하지 않음"awk 코드가 제대로 작동하지 않는 것을 발견했습니다. 또한 transpose.c 프로그램에서 --limit 플래그를 확인하십시오.이 플래그는 기본적으로 출력을 1k x 1k 차원으로 제한합니다.
ncemami

16

순수 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

이것은 흥미롭게도 테이블의 첫 번째 줄에 대한 디렉토리 목록을 인쇄하지만 내 파일에서 작동했습니다. 나는 이유를 알아 내기에 충분한 BASH를 모른다.
bugloaf

@bugloaf 테이블 모서리에 *가 있습니다.
Hello71

2
@bugloaf : 변수를 적절히 인용하면 다음을 방지 할 수 있습니다.printf "%s\t" "${array[$COUNTER]}"
추후 공지가있을 때까지 일시 중지됩니다.


9

다음은 작업을 수행하기위한 적당히 견고한 Perl 스크립트입니다. @ 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은 awk (0.026s 대 0.042s)보다 약간 뛰어난 성능을 보였습니다. 둘 다 문제가되지 않을 것입니다.


Perl 5.10.1 (32 비트) 대 awk ( '-V'가 부여 된 경우 버전 20040207) 대 MacOS X 10.5.8의 gawk 3.1.7 (32 비트)에 대한 대표적인 타이밍은 1 만 줄에 5 개 열이있는 파일입니다. 선:

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보다 느립니다. 분명히 귀하의 마일리지는 다를 것입니다.


내 시스템에서 gawk는 perl을 능가합니다. 내 편집 한 게시물에 내 결과를 볼 수 있습니다
ghostdog74

4
결론을 모았습니다 : 다른 플랫폼, 다른 소프트웨어 버전, 다른 결과.
ghostdog74



5

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

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

즉, 행을 반복 할 때 모든 필드 에 대해 해당 필드의 요소를 포함 f하는 ':'로 구분 된 문자열이 커 col[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

5

GNU 데이터 매시 는 한 줄의 코드와 잠재적으로 임의로 큰 파일 크기로이 문제에 완벽하게 적합합니다!

datamash -W transpose infile > outfile

3

hackish 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;

붙여 넣기 및 임시 파일을 사용하는 것은 불필요한 작업 일뿐입니다. 메모리 자체 내부에서 조작 할 수 있습니다 (예 : 배열 / 해시)
ghostdog74

2
네,하지만 그게 모든 것을 기억에 보관한다는 의미가 아닙니까? 내가 다루는 파일의 크기는 약 2-20GB입니다.
Federico Giorgi

3

자신의 예제에서 볼 수있는 유일한 개선 사항은 실행되는 프로세스 수와 프로세스간에 파이프되는 데이터 양을 줄이는 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

3

나는 일반적 awk으로이 요구 사항에 대해이 작은 스 니펫을 사용합니다 .

  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

이것은 모든 데이터를 2 차원 배열에로드 a[line,column]한 다음으로 다시 인쇄 a[column,line]하여 주어진 입력을 전치합니다.

이것은 max초기 파일에있는 열의 양을 추적하여 다시 인쇄 할 행 수로 사용되도록해야합니다.


2

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

2

나는 비슷한 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

2

모든 종류의 데이터 (숫자 또는 데이터)로 모든 종류의 행렬 (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

2

파일에서 단일 (쉼표로 구분 된) 줄 $ N 만 가져 와서 열로 바꾸려면 :

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

2

그다지 우아하지는 않지만이 "한 줄"명령은 문제를 빠르게 해결합니다.

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

여기에서 cols는 열 수이며 4를 head -n 1 input | wc -w.


2

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

2

일부 * nix 표준 유틸리티 한 줄로, 임시 파일이 필요하지 않습니다. 참고 : OP는 효율적인 수정 (즉, 더 빠름)을 원했으며 일반적으로 상위 답변이이 답변보다 빠릅니다. 한 줄짜리는 어떤 이유로 든 * nix 소프트웨어 도구 를 좋아하는 사람들을 위한 것입니다. 드물지만 ( 예 : 부족한 IO 및 메모리) 이러한 스 니펫은 실제로 일부 상위 답변보다 빠를 수 있습니다.

입력 파일 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

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

  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

2
그것들은 모두 awk 또는 perl 솔루션보다 훨씬 느리고 깨지기 쉽습니다. unix.stackexchange.com/questions/169716/… 읽기 .
Ed Morton

@EdMorton, 감사합니다. 속도 문제를 해결하기 위해 제 답변에 대한 소개를 qualifed. Re "fragile": not 3) , 그리고 프로그래머 주어진 기술에 대해 데이터가 안전하다는 것을 알 때 다른 것들도 ; POSIX 호환 쉘 코드가 perl 보다 더 안정적인 표준이 아닌가?
agc

죄송합니다. 펄에 대해 많이 알고 있습니다. 이 경우 사용할 도구는입니다 awk. cut, head, echo, 등 더보다 더 POSIX 호환 쉘 코드없는 awk스크립트입니다 - 그들은 모두 모든 UNIX 설치에 대한 표준입니다. awk를 사용할 수 있고 최종 결과가 더 빠르고 강력 할 때 입력 파일의 내용과 스크립트를 실행하는 디렉토리에 대해주의해야하는 도구 세트를 조합하여 사용할 이유가 없습니다. .
Ed Morton

나는 anti- awk 는 아니지만 조건은 다양합니다. 이유 # 1 : for f in cut head xargs seq awk ; do wc -c $(which $f) ; done 스토리지가 너무 느리거나 IO가 너무 낮을 때, 더 큰 인터프리터는 더 이상적인 상황에서 아무리 좋더라도 상황을 악화시킵니다. 이유 # 2 : awk (또는 대부분의 모든 언어)는 한 가지를 잘 수행하도록 설계된 작은 유틸리티보다 더 가파른 학습 곡선으로 어려움을 겪습니다. 런타임이 코더 맨 아워보다 저렴할 때 "소프트웨어 도구"로 쉽게 코딩하면 비용이 절약됩니다.
agc

1
#!/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


unix.stackexchange.com/questions/169716/… 을 읽어 해당 솔루션의 일부 문제 를 이해하십시오.
Ed Morton

1

또 다른 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

0

다음은 Haskell 솔루션입니다. -O2로 컴파일 할 때, 그것은 ghostdog의 awk보다 약간 더 빠르게 실행되고 반복 된 "Hello world"입력 라인에 대해 Stephan의 얇게 래핑 된 c 파이썬 보다 약간 느립니다 . 불행히도 명령 줄 코드 전달에 대한 GHC의 지원은 내가 말할 수있는 한 존재하지 않으므로 직접 파일에 작성해야합니다. 가장 짧은 행의 길이로 행을 자릅니다.

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

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

0

전체 어레이를 메모리에 저장하는 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

어느 것 (출력 행 수가 적 으면 이전 코드보다 빠름).


0

다음은 단순히 각 줄을 열로 변환 paste하고 함께 -ing 하는 Bash 한 줄입니다 .

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.

추신 : io 설명자를 사용하고 싶었지만 작동하지 못했습니다.


큰 파일에서 실행하려면 알람 시계를 설정해야합니다. unix.stackexchange.com/questions/169716/… 을 읽고 해당 접근 방식의 일부 문제 를 이해하십시오.
Ed Morton

0

R을 사용하는 oneliner ...

  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) "

0

이전에 유사한 작업을 수행하기 위해 아래 두 스크립트를 사용했습니다. 첫 번째는 "순수한"bash에있는 두 번째 것보다 훨씬 빠른 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.