नाम से कुछ कॉलम कैसे प्रिंट करें?


32

मेरे पास निम्न फ़ाइल है:

id  name  age
1   ed    50
2   joe   70   

मैं सिर्फ idऔर ageकॉलम छापना चाहता हूं । अभी मैं सिर्फ उपयोग करता हूं awk:

cat file.tsv | awk '{ print $1, $3 }'

हालाँकि, इसके लिए कॉलम संख्या जानना आवश्यक है। क्या ऐसा करने का कोई तरीका है जहां मैं कॉलम संख्या के बजाय कॉलम के नाम (पहली पंक्ति पर निर्दिष्ट) का उपयोग कर सकता हूं?


7
catआवश्यक नहीं है, BTW। आप उपयोग कर सकते हैंawk '{ print $1, $3 }' file.tsv
एरिक विल्सन

यदि कॉलम संख्या नहीं है , तो आप किस पर निर्भर रहना चाहेंगे?
rozcietrzewiacz

2
@rozcietrzewiacz नाम; वह कहते हैं कि करना चाहता है idके बजाय $1और ageके बजाय$3
माइकल Mrozek

यह भी देखना पर चर्चा stackoverflow
Hotschke

जवाबों:


37

शायद कुछ इस तरह:

$ cat t.awk
NR==1 {
    for (i=1; i<=NF; i++) {
        ix[$i] = i
    }
}
NR>1 {
    print $ix[c1], $ix[c2]
}
$ awk -f t.awk c1=id c2=name input 
1 ed
2 joe
$ awk -f t.awk c1=age c2=name input 
50 ed
70 joe

यदि आप कमांड लाइन पर प्रिंट करने के लिए कॉलम निर्दिष्ट करना चाहते हैं, तो आप कुछ ऐसा कर सकते हैं:

$ cat t.awk 
BEGIN {
    split(cols,out,",")
}
NR==1 {
    for (i=1; i<=NF; i++)
        ix[$i] = i
}
NR>1 {
    for (i in out)
        printf "%s%s", $ix[out[i]], OFS
    print ""
}
$ awk -f t.awk -v cols=name,age,id,name,id input 
ed 1 ed 50 1 
joe 2 joe 70 2 

( ब्लॉक -vमें परिभाषित चर प्राप्त करने के लिए स्विच पर ध्यान दें BEGIN।)


मैं सीखने में अजीब लगा रहा हूँ ... स्तम्भों की एक चर संख्या का समर्थन करने का सबसे अच्छा तरीका क्या है? awk -f t.awk col1 col2 ... coln inputआदर्श होगा; awk -f t.awk cols=col1,col2,...,coln inputयह भी काम करेगा
ब्रेट थॉमस 15

1
मेरे उत्तर को अपडेट किया। यदि आप इसके साथ सामान करना चाहते हैं, तो इसे सीखना बंद कर दें :)
Mat

3
दूसरा उदाहरण अपेक्षित क्रम में स्तंभों का उत्पादन नहीं करता है, for (i in out)कोई अंतर्निहित आदेश नहीं है। एक समाधान के रूप में gawkप्रदान PROCINFO["sorted_in"]करता है, एक के साथ सूचकांक पर पुनरावृति for( ; ; )शायद बेहतर है।
mr.spuratic

@BrettThomas, अत्यधिक इस ट्यूटोरियल की सलाह देते हैं । (यदि आपके पास lynda.com तक पहुंच है, तो मैं और भी अधिक "Awk Essential Training" की सिफारिश करता हूं, जो सभी समान सामग्री को कवर करता है, लेकिन अधिक संक्षेप में और अभ्यास अभ्यास के साथ।)
वाइल्डकार्ड

मिस्टर स्पुरैटिक, तुम दा आदमी। मैं (मैं बाहर) समस्या के लिए दौड़ा, ठीक w / 3 क्षेत्रों में काम किया, जब मैंने 2 जोड़ा तो यह 4,5,1,2,3 था, बजाय 1,2,3,4,5 की तरह मैं उम्मीद कर रहा था । उन्हें प्राप्त करने के लिए आपको (i = 1; i <= लंबाई (बाहर); i ++)
सेवेरन

5

बस बहुत में एक पर्ल समाधान trowing:

#!/usr/bin/perl -wnla

BEGIN {
    @f = ('id', 'age');   # field names to print
    print "@f";           # print field names
}

if ($. == 1) {            # if line number 1
    @n = @F;              #   get all field names
} else {                  # or else
    @v{@n} = @F;          #   map field names to values
    print "@v{@f}";       #   print values based on names
}

5

csvkit

एक csv प्रारूप करने के लिए इनपुट डेटा कन्वर्ट और जैसे एक csv उपकरण का उपयोग csvcutसे csvkit:

$ cat test-cols.dat 
id  name  age
1   ed    50
2   joe   70 

Csvkit स्थापित करें:

$ pip install csvkit

इसे एक वैध सीएसवी फ़ाइल में बदलने और लागू trकरने के -sलिए इसके निचोड़ विकल्प के साथ प्रयोग करें csvcut:

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age
id,age
1,50
2,70

यदि आप पुराने डेटा प्रारूप में वापस आना चाहते हैं, तो आप उपयोग कर सकते हैं tr ',' ' ' | column -t

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age | tr ',' ' ' | column -t
id  age
1   50
2   70

टिप्पणियाँ

  • csvkit भी विभिन्न सीमांकक ( साझा विकल्प -d या --delimiter) का समर्थन करता है , लेकिन एक csv फ़ाइल लौटाता है:

    • यदि फ़ाइल निम्न कार्यों के लिए कॉलम (अलग-अलग टैब नहीं) के लिए केवल रिक्त स्थान का उपयोग करती है

      $ csvcut -d ' ' -S -c 'id,age' test-cols.dat
      id,age
      1,50
      2,70
    • यदि फ़ाइल स्तंभों को अलग करने के लिए एक टैब का उपयोग करती है, तो निम्न कार्य और csvformattsv फ़ाइल को वापस लाने के लिए उपयोग किया जा सकता है:

      $ csvcut -t -c 'id,age' test-cols.dat | csvformat -T
      id  age
      1   50
      2   70

      जहाँ तक मैंने जाँच की है, केवल एक टैब की अनुमति है।

  • csvlook तालिका को एक मार्कडाउन तालिका प्रारूप में प्रारूपित कर सकते हैं

    $ csvcut -t -c "id,age" test-cols.dat | csvlook
    | id | age |
    | -- | --- |
    |  1 |  50 |
    |  2 |  70 |
  • UUOC (बिल्ली का बेकार उपयोग) : मुझे यह पसंद है कि मैं कमांड का निर्माण करूं।


+1। लेकिन अनावश्यक उपयोग trभी। TSV फ़ाइलों को सीधे CSV में बदलने की आवश्यकता के बिना, समर्थन किया जाता है। -t(उर्फ --tabs) विकल्प बताता cvscutक्षेत्र परिसीमक के रूप में टैब का उपयोग करने के लिए। और -dया --delimiterकिसी भी चरित्र को सीमांकक के रूप में उपयोग करने के लिए।
कैस

कुछ परीक्षण के साथ, ऐसा लगता है कि विकल्प -dऔर -tविकल्प अर्ध-टूटे हुए हैं। वे इनपुट सीमांकक को निर्दिष्ट करने के लिए काम करते हैं, लेकिन आउटपुट सीमांकक को हमेशा अल्पविराम होने के लिए हार्डकोड किया जाता है। IMO जो टूट गया है - यह या तो इनपुट सीमांकक के समान होना चाहिए या उपयोगकर्ता के पास आउटपुट परिसीमा सेट करने की अनुमति देने के लिए एक और विकल्प होना चाहिए, जैसे कि awkFS और OFS संस्करण।
कैस

4

यदि आप संख्याओं के बजाय केवल उन क्षेत्रों को उनके नाम से संदर्भित करना चाहते हैं , तो आप उपयोग कर सकते हैं read:

while read id name age
do
  echo "$id $age"
done < file.tsv 

संपादित करें

मैंने अंत में आपका अर्थ देखा! यहां एक बैश फ़ंक्शन है जो कमांड लाइन पर आपके द्वारा निर्दिष्ट केवल कॉलम को प्रिंट करेगा ( नाम से )।

printColumns () 
{ 
read names
while read $names; do
    for col in $*
    do
        eval "printf '%s ' \$$col"
    done
    echo
done
}

यहां बताया गया है कि आप इसे उस फ़ाइल के साथ उपयोग कर सकते हैं जिसे आपने प्रस्तुत किया है:

$ < file.tsv printColumns id name
1 ed 
2 joe 

(समारोह पढ़ता है stdin< file.tsv printColumns ... के बराबर है printColumns ... < file.tsvऔर cat file.tsv | printColumns ...)

$ < file.tsv printColumns name age
ed 50 
joe 70 

$ < file.tsv printColumns name age id name name name
ed 50 1 ed ed ed 
joe 70 2 joe joe joe

नोट: आपके द्वारा अनुरोधित कॉलम के नाम पर ध्यान दें! इस संस्करण में पवित्रता की जाँच का अभाव है, इसलिए यदि कोई एक तर्क कुछ है तो बुरा हो सकता है"anything; rm /my/precious/file"


1
इसके लिए कॉलम संख्या जानना भी आवश्यक है। सिर्फ इसलिए कि आप उन्हें नाम देते हैं id, nameऔर age, इस तथ्य को नहीं बदलते हैं कि आपकी readलाइन में ऑर्डर हार्ड-कोडेड है ।
जन्मोसेन

1
@janmoesen हां, मुझे आखिरकार बात मिल गई :)
rozcietrzewiacz

यह अच्छा है, धन्यवाद। मैं बड़ी फ़ाइलों (1000 कॉलम, लाखों पंक्तियों) के साथ काम कर रहा हूं इसलिए गति के लिए awk का उपयोग कर रहा हूं।
ब्रेट थॉमस

@BrettThomas ओह मैं देख रहा हूँ। मैं तब बहुत उत्सुक हूं: क्या आप कुछ बेंचमार्क पोस्ट कर सकते हैं जो समय की तुलना करता है? (उपयोग time { command(s); })
rozcietrzewiacz

@rozceitrewaicz:time cat temp.txt | ./col1 CHR POS > /dev/null 99.144u 38.966s 2:19.27 99.1% 0+0k 0+0io 0pf+0w time awk -f col2 c1=CHR c2=POS temp.txt > /dev/null 0.294u 0.127s 0:00.50 82.0% 0+0k 0+0io 0pf+0w
ब्रेट थॉमस 19

3

हांलांकि इसकी कीमत के बारे निश्चित नहीं हूँ। यह स्रोत में किसी भी संख्या में कॉलम और प्रिंट करने के लिए किसी भी संख्या में कॉलम को संभाल सकता है, जो भी आउटपुट अनुक्रम आप चुनते हैं; बस फिर से आर्ग की व्यवस्था ...

जैसे। फोन:script-name id age

outseq=($@)
colnum=($( 
  for ((i; i<${#outseq[@]}; i++)) ;do 
    head -n 1 file |
     sed -r 's/ +/\n/g' |
      sed -nr "/^${outseq[$i]}$/="
  done ))
tr ' ' '\t' <<<"${outseq[@]}"
sed -nr '1!{s/ +/\t/gp}' file |
  cut -f $(tr ' ' ','<<<"${colnum[@]}") 

उत्पादन

id      age
1       50
2       70

2

यदि आप जो फ़ाइल पढ़ रहे हैं वह संभवतः उपयोगकर्ता-जनित नहीं हो सकती है, तो आप रीड बिलिन का दुरुपयोग कर सकते हैं:

f=file.tsv
read $(head -n1 "$f") extra <<<`seq 100`
awk "{print \$$id, \$$age}" "$f"

इनपुट फ़ाइल की पूरी पहली पंक्ति को तर्क सूची में प्रतिस्थापित किया जाता है, इसलिए readहेडर लाइन से सभी फ़ील्ड नामों को चर नामों के रूप में पारित किया जाता है। इनमें से पहले को 1 seq 100उत्पन्न होता है, दूसरे को 2 मिलता है, तीसरे को 3 मिलता है और इसी तरह से। अतिरिक्त seqआउटपुट डमी चर द्वारा भिगोया जाता है extra। यदि आप समय से पहले इनपुट कॉलम की संख्या जानते हैं, तो आप 100 को मैच के लिए बदल सकते हैं और छुटकारा पा सकते हैंextra

awkस्क्रिप्ट एक डबल-कोटेड स्ट्रिंग, खोल चरों द्वारा परिभाषित की इजाजत दी है readके रूप में स्क्रिप्ट में प्रतिस्थापित किया जा करने के लिए awkक्षेत्र संख्या।


1

आमतौर पर फ़ाइल हेडर को देखना आसान होता है, आपके लिए आवश्यक कॉलम की संख्या की गणना करें ( सी ) और फिर यूनिक्स का उपयोग करें cut:

cut -f c -d, file.csv

लेकिन जब कई कॉलम या कई फाइलें होती हैं, तो मैं निम्नलिखित बदसूरत चाल का उपयोग करता हूं:

cut \
  -f $(head -1 file.csv | sed 's/,/\'$'\n/g' | grep -n 'column name' | cut -f1 -d,) \
  -d, \ 
  file.csv

OSX पर परीक्षण किया गया, file.csvअल्पविराम-रहित है।


1

यहाँ एक कॉलम चुनने के लिए एक त्वरित तरीका है।

कहें कि हम "फू" नाम का कॉलम चाहते हैं:

f=file.csv; colnum=`head -1 ${f} | sed 's/,/\n/g' | nl | grep 'foo$' | cut -f 1 `; cut -d, -f ${colnum} ${f}

मूल रूप से, हेडर लाइन लें, इसे प्रति पंक्ति एक कॉलम नाम के साथ कई लाइनों में विभाजित करें, लाइनों की संख्या, वांछित नाम के साथ लाइन का चयन करें और संबंधित लाइन नंबर को पुनः प्राप्त करें; फिर उस लाइन नंबर को कट कमांड के कॉलम नंबर के रूप में उपयोग करें।


0

इसी तरह के समाधान की तलाश में (मुझे आईडी नाम के कॉलम की आवश्यकता है, जिसमें एक अलग कॉलम संख्या हो सकती है), मैं इस एक पर आया:

head -n 1 file.csv | awk -F',' ' {
      for(i=1;i < NF;i++) {
         if($i ~ /id/) { print i }
      }
} '

0

मैंने इस उद्देश्य के लिए एक पायथन स्क्रिप्ट लिखी थी जो मूल रूप से इस तरह काम करती है:

with fileinput.input(args.file) as data:
    headers = data.readline().split()
    selectors = [any(string in header for string in args.fixed_strings) or
                 any(re.search(pat, header) for pat in args.python_regexp)
                 for header in headers]

    print(*itertools.compress(headers, selectors))
    for line in data:
        print(*itertools.compress(line.split(), selectors))

मैंने इसे हेडर grep केhgrep लिए कहा है , इसे इस तरह इस्तेमाल किया जा सकता है:

$ hgrep data.txt -F foo bar -P ^baz$
$ hgrep -F foo bar -P ^baz$ -- data.txt
$ grep -v spam data.txt | hgrep -F foo bar -P ^baz$

पूरी स्क्रिप्ट थोड़ी लंबी है, क्योंकि यह argparseकमांड लाइन तर्कों को पार्स करने के लिए उपयोग करता है और कोड निम्नानुसार है:

#!/usr/bin/python3

import argparse
import fileinput
import itertools
import re
import sys
import textwrap


def underline(s):
    return '\033[4m{}\033[0m'.format(s)


parser = argparse.ArgumentParser(
    usage='%(prog)s [OPTIONS] {} [FILE]'.format(
        underline('column-specification')),
    description=
        'Print selected columns by specifying patterns to match the headers.',
    epilog=textwrap.dedent('''\
    examples:
      $ %(prog)s data.txt -F foo bar -P ^baz$
      $ %(prog)s -F foo bar -P ^baz$ -- data.txt
      $ grep -v spam data.txt | %(prog)s -F foo bar -P ^baz$
    '''),
    formatter_class=argparse.RawTextHelpFormatter,
)

parser.add_argument(
    '-d', '--debug', action='store_true', help='include debugging information')
parser.add_argument(
    'file', metavar='FILE', nargs='?', default='-',
    help="use %(metavar)s as input, default is '-' for standard input")
spec = parser.add_argument_group(
    'column specification', 'one of these or both must be provided:')
spec.add_argument(
    '-F', '--fixed-strings', metavar='STRING', nargs='*', default=[],
    help='show columns containing %(metavar)s in header\n\n')
spec.add_argument(
    '-P', '--python-regexp', metavar='PATTERN', nargs='*', default=[],
    help='show a column if its header matches any %(metavar)s')

args = parser.parse_args()

if args.debug:
    for k, v in sorted(vars(args).items()):
        print('{}: debug: {:>15}: {}'.format(parser.prog, k, v),
              file=sys.stderr)

if not args.fixed_strings and not args.python_regexp:
    parser.error('no column specifications given')


try:
    with fileinput.input(args.file) as data:
        headers = data.readline().split()
        selectors = [any(string in header for string in args.fixed_strings) or
                     any(re.search(pat, header) for pat in args.python_regexp)
                     for header in headers]

        print(*itertools.compress(headers, selectors))
        for line in data:
            print(*itertools.compress(line.split(), selectors))

except BrokenPipeError:
    sys.exit(1)
except KeyboardInterrupt:
    print()
    sys.exit(1)

0

awk, इसके सभी विंटेज के लिए, स्वाभाविक रूप से पूर्णांक-अनुक्रमित है, जैसा कि है cut

यहां नाम-अनुक्रमित डेटा को संभालने के लिए कई उपकरण तैयार किए गए हैं (उनमें से अधिकांश सिर्फ सीएसवी और टीएसवी को संभालते हैं, जो बहुत लोकप्रिय फ़ाइल प्रारूप हैं):


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