Подводя итоги двух столбцов Unix способом - PullRequest
1 голос
/ 28 августа 2009

# Для устранения симптома

Как эффективно суммировать следующие столбцы?

Столбец 1

1
3
3
...   

Колонка 2

2323
343
232
...

Это должно дать мне

Ожидаемый результат

2324
346
235
...

У меня есть столбцы в двух файлах.


# Исходная ситуация

Иногда я использую слишком много фигурных скобок, так что я использовал еще один this {than this} в своих файлах. Я пытаюсь найти, где я использовал одну ненужную фигурную скобку. Я использовал следующие шаги для получения данных

Поиск команд

 find . * -exec grep '{' {} + > /tmp/1
 find . * -exec grep '}' {} + > /tmp/2

Команды AWK

 awk -F: '{ print $2 }' /tmp/1 > /tmp/11
 awk -F: '{ print $2 }' /tmp/2 > /tmp/22

Столбец находится в файлах / tmp / 11 и /tmp/22.

Я повторяю много похожих команд в моей процедуре. Это говорит о том, что это не правильный путь.

Пожалуйста, предложите мне любой способ, такой как Python, Perl или любой инструмент Unix, который может уменьшить количество шагов.

Ответы [ 6 ]

11 голосов
/ 28 августа 2009

Если c1 и c2 - ваши файлы, вы можете сделать это:

$ paste c1 c2 | awk '{print $1 + $2}'

Или (без AWK):

$ paste c1 c2 | while read i j; do echo $(($i+$j)); done
11 голосов
/ 28 августа 2009

Использование python:

totals = [ int(i)+int(j) for i, j in zip ( open(fname1), open(fname2) ) ]
3 голосов
/ 28 августа 2009

Вы можете избежать промежуточных шагов, просто используя команду, которая выполняет подсчет и сравнение одновременно:

find . -type f -exec perl -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g' {}\;

Это вызывает Perl-программу один раз для каждого файла, Perl-программа подсчитывает количество фигурных скобок каждого типа и печатает имя файла, если их количество не совпадает.

Вы должны быть осторожны с разделом /([}{]])/, find подумает, что нужно сделать замену на {}, если вы скажете /([{}]])/.

ВНИМАНИЕ: этот код будет содержать ложные и отрицательные результаты, если вы пытаетесь запустить его с исходным кодом. Рассмотрим следующие случаи:

сбалансировано, но фигурные скобки в строках:

if ($s eq '{') {
    print "I saw a {\n"
}

несбалансированный, но вьющиеся строки:

while (1) {
   print "}";

Вы можете развернуть команду Perl, используя B :: Deparse :

perl -MO = Deparse -nle 'END {напечатать $ ARGV, если $ h {"{"}! = $ H {"}"}} $ h {$ _} ++ для / ([} {]) / г '

Что приводит к:

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    sub END {
        print $ARGV if $h{'{'} != $h{'}'};
    }
    ;
    ++$h{$_} foreach (/([}{])/g);
}

Теперь мы можем взглянуть на каждый фрагмент программы:

BEGIN { $/ = "\n"; $\ = "\n"; }

Это вызвано параметром -l. Он устанавливает разделители входных и выходных записей в «\ n». Это означает, что все прочитанное будет разбито на записи на основе "\ n", и к любому оператору печати будет добавлено "\ n".

LINE: while (defined($_ = <ARGV>)) {
}

Это создается с помощью опции -n. Он перебирает каждый файл, переданный через командную строку (или STDIN, если файлы не передаются), читая каждую строку этих файлов. Это также происходит, когда $ARGV устанавливается последний файл, прочитанный <ARGV>.

chomp $_;

Это удаляет все, что находится в переменной $/ из строки, которую только что прочитали ($_), здесь ничего полезного не происходит. Это было вызвано опцией -l.

sub END {
    print $ARGV if $h{'{'} != $h{'}'};
}

Это блок END, этот код будет выполняться в конце программы. Он печатает $ARGV (имя файла, с которого последний раз считывали, см. Выше), если значения, хранящиеся в %h, связанные с клавишами '{' и '}', равны.

++$h{$_} foreach (/([}{])/g);

Это нужно разбить дальше:

/
    (    #begin capture
    [}{] #match any of the '}' or '{' characters
    )    #end capture
/gx

Это регулярное выражение, которое возвращает список символов '{' и '}', которые находятся в сопоставляемой строке. Поскольку строка не указана, переменная $_ (которая содержит строку, прочитанную последним из файла, см. Выше), будет сопоставлена. Этот список вводится в оператор foreach, который затем выполняет оператор, перед которым он находится, для каждого элемента (отсюда и имя) в списке. Он также устанавливает $_ (как вы видите, $_ - популярная переменная в Perl) как элемент из списка.

++h{$_}

Эта строка увеличивает значение в $ h, связанное с $_ (которое будет или '{' или '}', см. Выше), на единицу.

1 голос
/ 28 августа 2009

В Python (или Perl, Awk и т. Д.) Вы можете разумно сделать это за один отдельный «проход» - я не уверен, что вы подразумеваете под «слишком много фигурных скобок», но вы наверняка можете рассчитывать фигурное использование на файл. Например (если вам не нужно беспокоиться о файлах размером в несколько ГБ), 10 файлов используют большинство фигурных скобок:

import heapq
import os
import re

curliest = dict()

for path, dirs, files in os.walk('.'):
  for afile in files:
    fn = os.path.join(path, afile)
    with open(fn) as f:
      data = f.read()
      braces = data.count('{') + data.count('}')
    curliest[fn] = bracs

top10 = heapq.nlargest(10, curlies, curliest.get)
top10.sort(key=curliest.get)
for fn in top10:
  print '%6d %s' % (curliest[fn], fn)
0 голосов
/ 30 августа 2009

Ваша проблема может быть решена с помощью 1 команды awk ...

awk '{getline i<"file1";print i+$0}'  file2
0 голосов
/ 28 августа 2009

Ответ на ответ Lutz'n

Моя проблема была окончательно решена этим комнадом

paste -d: /tmp/1 /tmp/2 | awk -F: '{ print $1 "\t" $2 - $4 }'
...