Skuteczny sposób na transpozycję pliku w Bash

Mam ogromny plik rozdzielony tabulatorami sformatowany tak

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

Chciałbym przetransponować w efektywny sposób używając tylko poleceń Basha (mógłbym napisać w tym celu skrypt Perla o około dziesięciu liniach, ale powinien być wolniejszy w wykonaniu niż natywne funkcje Basha). Więc wyjście powinno wyglądać tak:

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

Myślałem o takim rozwiązaniu

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
Ale to powolne i nie wydaje się najskuteczniejszym rozwiązaniem. Widziałem rozwiązanie dla vi w to post , ale i tak jest zbyt wolno. Jakieś pomysły/sugestie / genialne pomysły? :-)
Author: Community, 2009-11-13

25 answers

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

Wyjście

$ 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

Wydajność wobec rozwiązania Perla Jonathana na pliku 10000 linii

$ 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
[[9]}EDIT by Ed Morton (@ghostdog74 zapraszam do usunięcia, jeśli nie zgadzasz się).

Być może ta wersja z bardziej wyraźnymi nazwami zmiennych pomoże odpowiedzieć na niektóre z poniższych pytań i ogólnie wyjaśni, co robi skrypt. Używa również tabulatorów jako separatora, o który pierwotnie prosił OP, aby obsłużyć puste pola i przypadkowo wyjście trochę dla tego konkretnego przypadku.

$ 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

Powyższe rozwiązania będą działać w każdym awk(oprócz starego, zepsutego awk oczywiście-tam YMMV).

Powyższe rozwiązania odczytują cały plik do pamięci - jeśli pliki wejściowe są na to zbyt duże, możesz to zrobić:

$ 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

Który prawie nie używa pamięci, ale odczytuje plik wejściowy raz na liczbę pól w linii, więc będzie znacznie wolniejszy niż wersja, która wczytuje cały plik do pamięci. Zakłada również liczba pól jest taka sama w każdej linii i używa GNU awk dla ENDFILE i ARGIND, ale każdy awk może zrobić to samo z testami na FNR==1 i END.

 93
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-10 15:39:03

Inną opcją jest użycie rs:

rs -c' ' -C' ' -T

-c zmienia separator kolumn Wejściowych, -C zmienia separator kolumn wyjściowych, a -T transponuje wiersze i kolumny. Nie używaj -t zamiast -T, ponieważ używa automatycznie obliczonej liczby wierszy i kolumn, która zwykle nie jest poprawna. rs, którego nazwa pochodzi od funkcji przekształcania w APL, pochodzi z BSD i OS X, ale powinna być dostępna w menedżerach pakietów na innych platformach.

Sekunda opcja jest użycie Ruby:

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

Trzecią opcją jest użycie jq:

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

jq -R . wypisuje każdą linię wejściową jako ciąg znaków JSON, -s (--slurp) tworzy tablicę dla linii wejściowych po przetworzeniu każdej linii jako JSON i -r (--raw-output) wypisuje zawartość ciągów zamiast liter ciągów JSON. Operator / jest przeciążony do podzielonych łańcuchów.

 39
Author: user4669748,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 08:10:50

Rozwiązanie Pythona:

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

Powyższe opiera się na następującej treści:

import sys

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

Ten kod zakłada, że każda linia ma taką samą liczbę kolumn(nie wykonuje się wypełnienia).

 28
Author: Stephan202,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-13 17:21:00

Projekt transpose na sourceforge jest programem C podobnym do coreutil do tego właśnie celu.

gcc transpose.c -o transpose
./transpose -t input > output #works with stdin, too.
 20
Author: flying sheep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-08 17:36:25

Czysty BASH, bez dodatkowego procesu. Fajne ćwiczenie:

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
 14
Author: Fritz G. Mehner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-19 17:51:31

Oto umiarkowanie solidny skrypt Perla do wykonania zadania. Istnieje wiele analogii strukturalnych z rozwiązaniem @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";
}

Przy wielkości danych próbki różnica w wydajności między perlem a awk była znikoma (1 milisekunda z 7 ogółem). Z większym zestawem danych (matryca 100x100, wpisy po 6-8 znaków), perl nieznacznie przewyższa awk-0,026 s vs 0,042 s. żadne z nich nie może być problemem.


Reprezentacyjne timingi dla Perla 5.10.1 (32-bit) vs awk (wersja 20040207, gdy podano '-V') vs gawk 3.1.7 (32-bit) na MacOS X 10.5.8 na pliku zawierającym 10 000 linii z 5 kolumnami w linii:

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: 

Zauważ, że gawk jest znacznie szybszy niż awk na tej maszynie, ale nadal wolniejszy niż perl. Oczywiście twój przebieg będzie się różnić.

 9
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-16 15:35:28

Spójrz na GNU datamash , który może być używany tak jak datamash transpose. Przyszła wersja będzie również obsługiwać tabelę krzyżową (tabele przestawne)

 7
Author: pixelbeat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 09:08:01

Jeśli masz sc zainstalowane, można zrobić:

psc -r < inputfile | sc -W% - > outputfile
 6
Author: Dennis Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-13 16:54:28

Zakładając, że wszystkie twoje wiersze mają taką samą liczbę pól, ten program awk rozwiązuje problem:

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

W słowach, gdy pętlujesz wiersze, dla każdego pola f wyrastaj oddzielony od siebie łańcuch col[f] zawierający elementy tego pola. Po zakończeniu wszystkich wierszy wydrukuj każdy z tych ciągów w osobnej linii. Następnie możesz zastąpić ':' separatorem, który chcesz (powiedzmy, spacją), przesyłając wyjście przez tr ':' ' '.

Przykład:

$ 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
 4
Author: Guilherme Freitas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-10 19:31:16

Jest do tego specjalnie skonstruowane narzędzie,

GNU datamash utility

apt install datamash  

datamash transpose < yourfile

Pobrane z tej strony, https://www.gnu.org/software/datamash/ i http://www.thelinuxrain.com/articles/transposing-rows-and-columns-3-methods

 4
Author: nelaaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-07 09:00:40

Jedyną poprawą, jaką widzę dla Twojego własnego przykładu, jest użycie awk, które zmniejszy liczbę uruchomionych procesów i ilość danych przesyłanych między nimi:

/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
Author: Simon C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-13 16:08:51

Hakerskie rozwiązanie Perla może być takie. Jest to miłe, ponieważ nie ładuje wszystkich plików w pamięci, drukuje pośrednie pliki tymczasowe, a następnie używa all-wonderful paste

#!/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;
 2
Author: Federico Giorgi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-13 15:49:11

Użyłem rozwiązania fgm (dzięki fgm!), ale potrzebne do wyeliminowania znaków tabulacji na końcu każdego wiersza, więc zmodyfikował skrypt tak:

#!/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
Author: dtw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-21 22:39:57

Właśnie szukałem podobnego Basha, ale z obsługą paddingu. Oto scenariusz, który napisałem w oparciu o rozwiązanie fgm, który wydaje się działać. Jeśli to może pomóc...

#!/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
Author: user3251704,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-30 05:27:17

Szukałem rozwiązania do transponowania dowolnego rodzaju macierzy (nxn lub mxn) z dowolnymi danymi (liczbami lub danymi) i otrzymałem następujące rozwiązanie:

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
Author: Another.Chemist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-06 04:41:33

Zwykle używam tego małego fragmentu awk do tego wymagania:

  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

To po prostu ładuje wszystkie dane do tablicy dwuwymiarowej a[line,column], a następnie drukuje je jako a[column,line], aby transponować dane wejściowe.

To musi śledzić max imum ilość kolumn, które ma plik początkowy, tak, że jest używana jako liczba wierszy do wydrukowania z powrotem.

 2
Author: fedorqui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-12 07:48:30

Jeśli chcesz wyciągnąć z pliku tylko jedną (rozdzielaną przecinkami) linię $n i zamienić ją w kolumnę:

head -$N file | tail -1 | tr ',' '\n'
 2
Author: allanbcampbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-26 13:03:55

Niezbyt eleganckie, ale to polecenie" Jednowierszowe " szybko rozwiązuje problem:

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

Tutaj cols jest liczbą kolumn, gdzie można zastąpić 4 przez head -n 1 input | wc -w.

 2
Author: Felipe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-01 03:47:20
#!/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

Kolejna wersja z set eval

 1
Author: Dyno Fu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-19 07:43:32

Inne rozwiązanie awk i ograniczone dane wejściowe o wielkości posiadanej pamięci.

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

To łączy wszystkie te same pozycje liczb zespolonych i in END wypisuje wynik, który byłby pierwszym wierszem w pierwszej kolumnie, drugim wierszem w drugiej kolumnie, itd. Wyjście:

X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
 1
Author: αғsнιη,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-19 16:51:14

Oto rozwiązanie Haskella. Po skompilowaniu z-O2, działa nieco szybciej niż awk ghostdoga i nieco wolniej niż cienko owinięty C python Stephana na mojej maszynie dla powtarzających się linii wejściowych "Hello world". Niestety wsparcie GHC dla przekazywania kodu linii poleceń nie istnieje, o ile mogę powiedzieć, więc będziesz musiał napisać go do pliku samodzielnie. Spowoduje to obcięcie wierszy do długości najkrótszego wiersza.

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

main :: IO ()
main = interact $ unlines . map unwords . transpose . map words . lines
 0
Author: stelleg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-26 03:03:40

Rozwiązanie awk, które przechowuje całą tablicę w pamięci

    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

Ale możemy" chodzić " po Pliku tyle razy, ile potrzeba wierszy wyjściowych:

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

Który (dla małej liczby wierszy wyjściowych jest szybszy niż poprzedni kod).

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-28 22:46:04

Niektóre * Nix standard util jednolinijkowe, nie potrzebne pliki tymczasowe. Uwaga: OP chciał efektywny naprawić, (tzn. szybciej), a najlepsze odpowiedzi są zwykle szybsze. Te one-linery są dla tych, którzy lubią *nix "narzędzia programowe", z jakichkolwiek powodów. W rzadkich przypadkach (np. skąpe IO i pamięć) fragmenty te mogą być szybsze.

Wywołanie pliku wejściowego foo.

  1. Jeśli wiemy foo ma cztery kolumny:

    for f in 1 2 3 4 ; do cut -d ' ' -f $f foo | xargs echo ; done
    
  2. Jeśli nie wiemy ile kolumn foo mA:

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

    xargs ma limit rozmiaru i dlatego zrobi niekompletną pracę z długim plikiem. Jaki limit wielkości jest zależny od systemu, np.:

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

    Maksymalna długość polecenia, którego możemy użyć: 2088944

  3. tr & echo:

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

    ...lub jeśli # kolumn jest nieznany:

    n=$(head -n 1 foo | wc -w)
    for f in $(seq 1 $n); do cut -d ' ' -f $f foo | tr '\n\ ' ' ; echo ; done
    
  4. Za pomocą set, które jak xargs, ma podobne ograniczenia w zależności od rozmiaru wiersza poleceń:

    for f in 1 2 3 4 ; do set - $(cut -d ' ' -f $f foo) ; echo $@ ; done
    
 0
Author: agc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-21 15:33:26

Oto Bash one-liner, który opiera się na prostej konwersji każdej linii do kolumny ipaste-ing them together:

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. Tworzy plik tmp1, aby nie był pusty.

  2. Odczytuje każdą linię i przekształca ją w kolumnę za pomocą tr

  3. Wkleja nową kolumnę do pliku tmp1

  4. Kopiuje wynik z powrotem do tmp1.

PS: naprawdę chciałem użyć io-deskryptorów, ale nie mogłem ich zmusić do praca.

 0
Author: kirill_igum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-19 07:35:47

GNU datamash ( https://www.gnu.org/software/datamash ) doskonale nadaje się do tego problemu z tylko jedną linią kodu i potencjalnie arbitralnie dużym rozmiarem pliku! datamash-W transponuje input_file.txt > input_file_transposed.txt

 0
Author: Pal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 15:00:34