Удалить дубликаты в каждой строке файла

1871
Arash

Как я могу удалить дубликаты в каждой строке, например, здесь?

1 1 1 2 1 2 3 5 5 4 1 2 3 3 

Я хотел бы получить этот вывод:

1 2 3  5 4 1 2 3 

Есть много строк (100 000), и в каждой строке я хочу уникальные значения. Perl может быть самым быстрым, но как я могу сделать это в Perl или Bash?

7

3 ответа на вопрос

12
nerdwaller

Here is an option using awk:

awk '{ while(++i<=NF) printf (!a[$i]++) ? $i FS : ""; i=split("",a); print ""}' infile > outfile 

Edit Updated with comments:

  1. while (++i<=NF)

    Initializes the while loop, precrementing "i" since $0 is the full line in awk.

    So it starts at $1 (first field). Loops through the line until the end (less than or equal to 'NF' which is built into awk for "Number of Fields"). The default field separator is a space, you could change the default separator easily.

  2. printf (!a[$i]++) ? $i FS : ""

    This is a ternary operation.

    So, if input is not in the array !a[$i]++, then it prints $i, if it is, it prints "". (You could remove the ! and reverse the $i FS : "" if you don't like it this way).

  3. i=split("",a)

    Normally, that's a null split. In this case, it resets I for the next line.

  4. print ""

    ends the line for the output (not 100% why, actually), otherwise you would have an output of:

    1 2 3 5 4 1 2 3 instead of
    1 2 3
    5 4 1 2 3

Чтобы помочь нынешним и будущим читателям, постарайтесь документировать ответы в некоторой степени. Это компактно и эффективно, но совершенно нечитаемо для кого-то, кто не очень привык к «awk», так как он опирается на порядок тестирования и работы, троичный оператор, «split (», a) «причуду для сброса массива (и его возвращаемое значение для сброса `i`) и специальные переменные` NF` и `FS`. Такое объяснение делает ответ еще лучше! Daniel Andersson 11 лет назад 5
@DanielAndersson Мои извинения за ленивость, обновлено. Спасибо! nerdwaller 11 лет назад 0
nerdwaller: причина, по которой вы получаете 1 2 3 5 4 1 2 3 без шага 4, состоит в том, что весь ваш вывод выполняется через printf, без \ n \ n когда-либо добавленных ... tink 11 лет назад 1
Шаг 2 работает, так как он увеличивает значение массива с индексом текущего числа. Если этот индекс был пустым, тест возвращает `! False`, и приращение выполняется _after_ сравнения. В следующий раз, когда цикл найдет то же число, сравнение вернет `! True`, так как значение, соответствующее индексу, было установлено в значение в последний раз. Поле снова увеличивается, но это «общее количество» не используется позже (хотя это не повредит). Daniel Andersson 11 лет назад 0
На шаге 3 массив `a` удаляется для следующей итерации строки. `split (" ", a)` является сокращением для удаления массива `a` (см. [документацию] (http://www.gnu.org/software/gawk/manual/html_node/Delete.html#fn- 1) для уведомления). Как побочный эффект, эта операция также возвращает `0`, и, поскольку` i` должен быть установлен в `0` для следующей итерации, вместо разделения` i = 0 вместо вызова i используется вызов `split ()`. `вызов, сохранение некоторых символов (возможно, за счет читабельности). Daniel Andersson 11 лет назад 0
5
slhck

Since ruby comes with any Linux distribution I know of:

ruby -e 'STDIN.readlines.each { |l| l.split(" ").uniq.each { |e| print "# " }; print "\n" }' < test 

Here, test is the file that contains the elements.

To explain what this command does—although Ruby can almost be read from left to right:

  • Read the input (which comes from < test through your shell)
  • Go through each line of the input
  • Split the line based on one space separating the items, into an array (split(" "))
  • Get the unique elements from this array (in-order)
  • For each unique element, print it, including a space (print "# ")
  • Print a newline once we're done with the unique elements
2
glenn jackman

Not pure bash, but ...:

while read line; do printf "%s\n" $line | sort -u | tr '\n' ' ' echo '' done < file 

The lines will be sorted as a byproduct.

Похожие вопросы