बैश में एक फ़ाइल को स्थानांतरित करने का एक कुशल तरीका


110

मेरे पास इस तरह से स्वरूपित एक विशाल टैब-अलग फ़ाइल है

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

मैं इसे केवल बैश कमांड का उपयोग करके एक कुशल तरीके से स्थानांतरित करना चाहता हूं (मैं ऐसा करने के लिए एक दस या इतनी लाइनें पर्ल स्क्रिप्ट लिख सकता था, लेकिन यह देशी बैश फ़ंक्शन की तुलना में निष्पादित करने के लिए धीमा होना चाहिए)। तो आउटपुट जैसा दिखना चाहिए

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
क्या आपको लगता है कि एक बैश स्क्रिप्ट मौजूद होगी जो पर्ल स्क्रिप्ट से तेज होगी? यह ठीक उसी तरह की समस्या है, जिस पर पर्ल का प्रभाव पड़ता है।
मार्क पिम

1
@मार्क, यदि इसका शुद्ध बैश है, तो यह उन सभी कट / सेड आदि टूल्स को एक साथ चलाने से तेज हो सकता है। लेकिन फिर से, यदि आप "बश" को उपकरणों के संयोजन के रूप में परिभाषित करते हैं, तो बस एक अजीब स्क्रिप्ट लिखना पर्ल राइट पाठ प्रसंस्करण के लिए तुलनीय होगा।
भूतडॉग .४

यह समझने के लिए कि यहां पर्ल कितना धीमा होगा, एक और जोड़ें। कोड लिखने के लिए धीमा है? निष्पादित करने के लिए धीमा? मैं वास्तव में पर्ल को नापसंद करता हूं, लेकिन यह इस तरह के काम में उत्कृष्टता हासिल करता है।
कोरी पोर्टर

यदि आपके कॉलम / फ़ील्ड में एक निश्चित आकार / चौड़ाई है, तो आप अपनी फ़ाइल को मेमोरी में पढ़ने से बचने के लिए पायथन फ़ाइल का उपयोग कर सकते हैं। क्या आपके पास निश्चित स्तंभ / फ़ील्ड आकार / चौड़ाई है?
tommy.carstensen 23

2
जो कोई भी शेल स्क्रिप्ट को सोचता है, वह awk या perl की तुलना में अधिक तेज़ होगा। इसे unix.stackexchange.com/questions/169716/ पर पढ़ना होगा ताकि वे समझ सकें कि ऐसा क्यों नहीं है।
एड मॉर्टन

जवाबों:


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 लाइनों फ़ाइल पर जोनाथन द्वारा पर्ल समाधान के खिलाफ प्रदर्शन

$ 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 (@ ghostdog74 हटाने के लिए स्वतंत्र महसूस यदि आप अस्वीकृत)।

शायद कुछ अधिक स्पष्ट चर नामों वाला यह संस्करण नीचे दिए गए कुछ सवालों के जवाब देने में मदद करेगा और आम तौर पर यह स्पष्ट करेगा कि स्क्रिप्ट क्या कर रही है। यह विभाजक के रूप में भी टैब का उपयोग करता है जिसे ओपी ने मूल रूप से कहा था इसलिए यह खाली क्षेत्रों को संभालता है और यह संयोग से इस विशेष मामले के लिए आउटपुट को थोड़ा बढ़ा देता है।

$ 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==1और टेस्ट के साथ भी ऐसा कर सकता है END


और अब पंक्ति और स्तंभ लेबल भी संभालना है?
जोनाथन लेफ़लर

ठीक है - तुम सही हो; आपका नमूना डेटा प्रश्न के नमूना डेटा से मेल नहीं खाता है, लेकिन आपका कोड प्रश्न के नमूना डेटा पर ठीक काम करता है और आवश्यक आउटपुट (खाली बनाम टैब रिक्ति देता है) लेता है। मुख्य रूप से मेरी गलती है।
जोनाथन लेफ़लर

दिलचस्प समय - मैं मानता हूँ कि आपको awk में प्रदर्शन लाभ दिखाई देगा। मैं MacOS X 10.5.8 का उपयोग कर रहा था, जो 'gawk' का उपयोग नहीं करता है; और मैं पर्ल 5.10.1 (32-बिट बिल्ड) का उपयोग कर रहा था। मैं इकट्ठा करता हूं कि आपका डेटा प्रति पंक्ति 4 कॉलम के साथ 10000 लाइनें था? वैसे भी, यह बहुत बड़ी बात नहीं है; awk और perl दोनों व्यवहार्य समाधान हैं (और awk solution neater है - मेरे पर्ल में 'परिभाषित' चेक सख्त / चेतावनियों के तहत फ्री रन चेतावनी देने के लिए आवश्यक हैं) और न ही कोई स्लाउच है और दोनों के मूल की तुलना में तेज़ होने की संभावना है शेल स्क्रिप्ट समाधान।
जोनाथन लेफलर

मेरे मूल 2.2GB मैट्रिक्स पर, पर्ल समाधान awk से थोड़ा तेज है - 350.103s बनाम 369.410s मैं पर्ल प्रति 5.8.8 64bit का उपयोग कर रहा था
Federico Giorgi

1
@ zx8754 कि अधिकतम संख्या वाले फ़ील्ड केवल एक पुराने, गैर-पॉसिक्स awk पर लागू होते हैं। संभवतः अविश्वसनीय रूप से दुर्भाग्य से "नवाक" नाम दिया गया। यह gawk या अन्य आधुनिक awks पर लागू नहीं होता है।
एड मॉर्टन

47

एक अन्य विकल्प का उपयोग करना है rs:

rs -c' ' -C' ' -T

-cइनपुट कॉलम विभाजक को -Cबदलता है, आउटपुट कॉलम विभाजक को बदलता है, और -Tपंक्तियों और स्तंभों को स्थानांतरित करता है। -tइसके बजाय का उपयोग न करें -T, क्योंकि यह स्वचालित रूप से गणना की गई पंक्तियों और स्तंभों का उपयोग करता है जो आमतौर पर सही नहीं होते हैं।rs, जिसे एपीएल में फेरबदल फ़ंक्शन के नाम पर रखा गया है, बीएसडी और ओएस एक्स के साथ आता है, लेकिन यह अन्य प्लेटफार्मों पर पैकेज प्रबंधकों से उपलब्ध होना चाहिए।

एक दूसरा विकल्प रूबी का उपयोग करना है:

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अकेले एक टैब में इनपुट कॉलम विभाजक सेट करता है।
निसेतमा

2
@ लालेबर्डे, एक टैब चरित्र प्राप्त करने के लिए बैश के ANSI-C को $'\t'
आज़माएं

3
यह एक चरम मामला है, लेकिन कई पंक्तियों के साथ एक बहुत बड़ी फ़ाइल के लिए TTC TTA TTC TTC TTT, जैसे कि रनिंग rs -c' ' -C' ' -T < rows.seq > cols.seqदेता है rs: no memory: Cannot allocate memory। यह एक सिस्टम है जो 32 जीबी रैम के साथ FreeBSD 11.0-RELEASE चल रहा है। इसलिए, मेरा अनुमान है कि rsसब कुछ रैम में रखा जाता है, जो गति के लिए अच्छा है, लेकिन बड़े डेटा के लिए नहीं।
jrm

1
jq ने 766MB फ़ाइल पर 21Gb RAM का उपयोग किया। मैंने इसे 40 मिनट बाद बिना किसी आउटपुट के मार दिया।
ग्लुबड्रब

30

एक पायथन समाधान:

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
यहां एक छोटी समस्या: (पायथन 2.7) l.split()द्वारा प्रतिस्थापित l.strip().split(), अन्यथा आउटपुट की अंतिम पंक्ति अपंग है। मनमाना स्तंभ विभाजकों के लिए काम करता है, उपयोग करें l.strip().split(sep)और sep.join(c)यदि आपका विभाजक चर में संग्रहीत है sep
krlmlr

21

पक्षांतरित sourceforge पर परियोजना वास्तव में उस के लिए एक coreutil की तरह सी कार्यक्रम है।

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

लिंक के लिए धन्यवाद। हालाँकि, बड़ी मैट्रिसेस / फ़ाइलों से निपटने के लिए बहुत अधिक मेमोरी की आवश्यकता होती है।
tommy.carstensen

इसमें अवरुद्ध और फ़ील्ड के लिए तर्क दिए गए हैं: तर्कों -bऔर -fतर्कों को जोड़ने की कोशिश करें ।
उड़ने वाली भेड़

डिफ़ॉल्ट ब्लॉक आकार (--ब्लॉक या -बी) 10kb है और डिफ़ॉल्ट फ़ील्ड आकार (--fieldmax या -f) 64 है, ताकि यह नहीं हो सके। मैंने कोशिश की। फिर भी सुझाव के लिए धन्यवाद।
tommy.carstensen

1
2 जीबी आकार के एक सीएसवी के साथ अच्छी तरह से काम किया।
शिष्य

2
आयामों के साथ 5k द्वारा 11k के साथ एक मैट्रिक्स फ़ाइल के लिए, मैंने ट्रांसडॉस.टेक को ~ 7x तेज़ और ~ 5x अधिक मेमोरी-कुशल घोस्टडॉग74 के पहले जाग समाधान की तुलना में कुशल पाया। इसके अलावा, मैंने पाया कि "ghostdog74 से awk कोड" लगभग कोई मेमोरी का उपयोग नहीं करता है। इसके अलावा, transpose.c कार्यक्रम में - मुख्य ध्वज के लिए देखें, जो डिफ़ॉल्ट रूप से आउटपुट को 1k से 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 आपकी तालिका में कोने में एक * है।
हैलो ०

2
@bugloaf: चर को उचित रूप से उद्धृत करने से रोकना चाहिए:printf "%s\t" "${array[$COUNTER]}"
जब तक कि अगली सूचना तक

16

GNU डेटामैश पर एक नज़र डालें, जिसे इस्तेमाल किया जा सकता है datamash transpose। भविष्य का संस्करण क्रॉस टेबुलेशन (पिवट टेबल) का भी समर्थन करेगा


9

यहाँ काम करने के लिए एक मामूली ठोस पर्ल स्क्रिप्ट है। @ 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";
}

नमूना डेटा आकार के साथ, पर्ल और ऑक के बीच प्रदर्शन अंतर नगण्य था (7 कुल में से 1 मिलीसेकंड)। एक बड़ा डेटा सेट (100x100 मैट्रिक्स, 6-8 अक्षर प्रत्येक में प्रविष्टियाँ) के साथ, थोड़ा बाहर निकला हुआ awk - 0.026s बनाम 0.042s। न ही समस्या होने की संभावना है।


Perl के लिए प्रतिनिधि समय 5.10.1 (32-बिट) बनाम awk (संस्करण 20040207 जब '-V' दिया गया) बनाम GOSk 3.1.7 (32-बिट) MacOS X 10.5.8 पर 5 लाइनों के साथ 10,000 कॉलम वाली फाइल पर 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 outperforms perl। आप मेरे संपादित पोस्ट
ghostdog74

4
निष्कर्ष निकाला गया: अलग-अलग मंच, अलग-अलग सॉफ्टवेयर संस्करण, अलग-अलग परिणाम।
भूतडॉग ghost४

6

यदि आपने scस्थापित किया है, तो आप कर सकते हैं:

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

4
ध्यान दें कि यह सीमित संख्या में लाइनों का समर्थन करता है क्योंकि scइसके स्तंभों को एक या दो वर्णों के संयोजन के रूप में नामित किया गया है। सीमा है 26 + 26^2 = 702
Thor


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 पर्ल समाधान इस तरह हो सकता है। यह अच्छा है क्योंकि यह मेमोरी में सभी फ़ाइल को लोड नहीं करता है, मध्यवर्ती अस्थायी फ़ाइलों को प्रिंट करता है, और फिर सभी-अद्भुत पेस्ट का उपयोग करता है

#!/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 आकार की हैं।
फेडेरिको जियोर्गी

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

यह सिर्फ सभी डेटा को एक द्विदिश सरणी में लोड करता है 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

मैं बस इसी तरह के बैश ट्रांसपोज़ की तलाश में था लेकिन पैडिंग के लिए समर्थन के साथ। यहाँ वह स्क्रिप्ट है जो मैंने 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

यहां कॉलम्स कॉलम की संख्या है, जहां आप 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

कुछ * निक्स मानक एक-लाइनर का उपयोग करते हैं, कोई अस्थायी फ़ाइलों की आवश्यकता नहीं होती है। नायब: ओपी एक कुशल फिक्स (यानी तेज) चाहता था , और शीर्ष उत्तर आमतौर पर इस उत्तर से तेज होते हैं। ये एक-लाइनर उन लोगों के लिए हैं जो * निक्स सॉफ़्टवेयर टूल पसंद करते हैं , जो भी कारणों से। दुर्लभ मामलों में, ( उदाहरण के लिए) दुर्लभ IO और मेमोरी), ये स्निपेट्स वास्तव में शीर्ष के कुछ उत्तरों की तुलना में तेज़ हो सकते हैं।

इनपुट फ़ाइल foo पर कॉल करें ।

  1. अगर हम जानते हैं कि फू में चार कॉलम हैं:

    for f in 1 2 3 4 ; do cut -d ' ' -f $f foo | xargs echo ; done
  2. अगर हमें पता नहीं है कि कितने कॉलम फू हैं:

    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/… पढ़ें ।
एड मॉर्टन

@EdMorton, धन्यवाद, मेरे उत्तर के पात्र ने आपकी गति संबंधी चिंताओं को दूर करने के लिए परिचय दिया। पुन: "नाजुक": 3 नहीं ) , और न ही अन्य जब प्रोग्रामर जानता है कि डेटा किसी दिए गए तकनीक के लिए सुरक्षित है; और POSIX संगत शेल कोड पर्ल से अधिक स्थिर मानक नहीं है ?
एसीसी

क्षमा करें, प्रति के बारे में बहुत कुछ। इस मामले में उपयोग करने के लिए उपकरण होगा awkcut, head, echo, आदि कोई और अधिक POSIX संगत खोल कोड एक से हैं awkस्क्रिप्ट है - वे सभी हर यूनिक्स स्थापना पर मानक हैं। संयोजन का उपयोग करने का कोई कारण नहीं है कि संयोजन में आपको अपनी इनपुट फ़ाइल की सामग्री के बारे में सावधानी बरतने की आवश्यकता होती है और जिस निर्देशिका से आप स्क्रिप्ट का निष्पादन करते हैं वह केवल awk का उपयोग कर सकता है और अंतिम परिणाम तेजी से और साथ ही अधिक मजबूत होता है। ।
एड मॉर्टन

कृपया, मैं नहीं कर रहा हूँ रोधी awk , लेकिन स्थिति बदलती हैं। कारण # 1: for f in cut head xargs seq awk ; do wc -c $(which $f) ; done जब भंडारण बहुत धीमा होता है या IO बहुत कम होता है, तो बड़े दुभाषिए चीजों को बदतर बना देते हैं, चाहे कितनी भी अच्छी हो वे सभी आदर्श परिस्थितियों में कितने अच्छे होंगे। कारण # 2: awk , (या अधिकांश किसी भी भाषा), भी एक steeper सीखने की अवस्था से ग्रस्त है एक छोटे से उपयोग की तुलना में अच्छी बात करने के लिए डिज़ाइन किया गया है। जब रन-टाइम कोडर मैन आवर्स से सस्ता होता है, तो "सॉफ्टवेयर टूल्स" के साथ आसान कोडिंग से पैसे की बचत होती है।
20

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/… पढ़ें , लेकिन सभी नहीं।
एड मॉर्टन

1

एक और बैश वैरिएंट

$ 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

यहाँ एक हास्केल समाधान है। जब -O2 के साथ संकलित किया जाता है, तो यह घोस्टडॉग के जाग से थोड़ा तेज़ चलता है और स्टेफ़न के पतले लिपटे सी पायथन की तुलना में थोड़ा "धीमी दुनिया" इनपुट लाइनों के लिए थोड़ा धीमा होता है । दुर्भाग्य से कमांड लाइन कोड पास करने के लिए जीएचसी का समर्थन गैर-मौजूद है जहां तक ​​मैं बता सकता हूं, इसलिए आपको इसे एक फाइल पर लिखना होगा। यह सबसे छोटी पंक्ति की पंक्तियों को काट देगा।

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

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

0

एक अजीब समाधान जो स्मृति में पूरे सरणी को संग्रहीत करता है

    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उन्हें एक साथ जोड़ने पर आधारित है:

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/… पढ़ें , लेकिन सभी नहीं।
एड मॉर्टन

0

आर का उपयोग कर एक 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

मैंने पहले भी इसी तरह के ऑपरेशन करने के लिए दो स्क्रिप्ट्स का उपयोग किया है। पहला जागने में है जो दूसरे से बहुत तेज है जो "शुद्ध" बैश में है। आप इसे अपने स्वयं के अनुप्रयोग के लिए अनुकूलित करने में सक्षम हो सकते हैं।

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.