2013-04-18 6 views
4

Предположим, что у меня есть CSV-файл с заголовками следующего вида:Подобрав полей CSV по имени, используя AWK

Field1,Field2 
3,262000 
4,449000 
5,650000 
6,853000 
7,1061000 
8,1263000 
9,1473000 
10,1683000 
11,1893000 

Я хотел бы написать сценарий AWK, который отвезет разделенный запятыми список поля имена target, разделите его на массив, а затем выберите только те столбцы с именами, которые я указываю.

Это то, что я пытался до сих пор, и я проверил, что head массива содержит нужные заголовки, а targets массива содержит желаемые цели, передаваемые в данной командной строке.

BEGIN{ 
    FS="," 
    split(target, targets, ",") 

} 

NR==1 { 
    for (i = 1; i <= NF; i++) head[i] = $i 
} 

NR !=1{ 
    for (i = 1; i <= NF; i++) { 
     if (head[i] in targets){ 
      print $i 
     } 
    } 
} 

Когда я вызвать этот сценарий с помощью команды

AWK -v цель = FIELD1 -f GetCol.awk Debug.csv

Я ничего не получить распечатанный.

ответ

8

Я понял это и отправляю ответ, если другие сталкиваются с той же проблемой.

Это связано с ключевым словом in, которое я использую для тестирования членства в массиве. Это ключевое слово проверяет, является ли операнд слева одним из индексов в массиве справа, а не на значениях. Исправление состоит в том, чтобы создать массив обратного поиска, как показано ниже.

BEGIN{ 
    OFS=FS="," 
    split(target, t_targets, ",") 
    for (i in t_targets) 
     targets[t_targets[i]] = i 
} 
+0

+1 для выяснения себя! Я добавил полный сценарий для работы, вы можете найти некоторые из них полезными. –

5

Мои два цента:

BEGIN{ 
    OFS=FS="," 
    split(target,fields,FS)   # We just set FS don't hard the comma here 
    for (i in fields)     # Distinct var name to aviod headaches 
     field_idx[fields[i]] = i  # Reverse lookup 
} 
NR==1 {        # Process header 
    for (i=1;i<=NF;i++)    # For each field header 
     head[i] = $i     # Add to hash for comparision with target 
    next        # Skip to next line 
} 
{          # Don't need invert condition (used next) 
    sep=""        # Set for leading separator 
    for (i=1;i<=NF;i++)    # For each field 
     if (head[i] in field_idx) { # Test for current field is a target field 
      printf "%s%s",sep,$i  # Print the column if matched 
      sep=OFS     # Set separator to OFS     
    } 
    printf "\n"      # Print newline character 
} 
1

Расширение @ sudo_O, решение которого (спасибо), что

  1. выводит поля из стандартного ввода на основе аргументов командной строки,
  2. выводит поля в запрошенном порядке (возможно, несколько раз),
  3. выводит заполнитель когда поле было запрошено, но не найдено, и
  4. предупреждает о стандартной ошибке о повторяющихся именах полей в заголовке.
#!/usr/bin/awk -f 
# Process standard input outputting named columns provided as arguments. 
# 
# For example, given foo.dat containing 
#  a b c c 
#  1a 1b 1c 1C 
#  2a 2b 2c 2C 
#  3a 3b 3c 3C 
# Running 
# cat foo.dat | ./namedcols c b a a d 
# will output 
# 1c 1b 1a 1a d 
# 2c 2b 2a 2a d 
# 3c 3b 3a 3a d 
# and will warn on standard error that it 
# Ignored duplicate 'c' in column 4 
# Notice that the requested but missing column d contains "d". 
# 
# Using awk's -F feature it is possible to parse comma-separated data: 
# cat foo.csv | ./namedcols -F, c b a a d 
BEGIN { 
    for (i=1; i<ARGC; ++i) 
     desired[i] = ARGV[i] 
    delete ARGV 
} 
NR==1 { 
    for (i=1; i<=NF; i++) 
     if ($i in names) 
      printf "Ignored duplicate '%s' in column %d\n", $i, i | "cat 1>&2" 
     else 
      names[$i] = i 
    next 
} 
{ 
    for (i=1; i<ARGC; ++i) 
     printf "%s%s",           \ 
       (i==1 ? "" : OFS),        \ 
       ((ndx = names[name = desired[i]])>0 ? $ndx: name) 
    printf RS 
} 
Смежные вопросы