Как найти уникальные символы в строке ввода?

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

Я знаю, что я могу найти уникальные строки файла, используя

sort -u file

Я хотел бы определить уникальные символы каждой строки (что-то вроде sort -u для каждой линии).

чтобы уточнить: учитывая этот ввод:

111223234213
111111111111
123123123213
121212122212

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

1234
1
123
12

7 ответов


использование sed

sed ':;s/\(.\)\(.*\)//;t' file

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

t является тестом и переходит к : метка, если предыдущая команда была успешной. Затем это повторяется до s/// команда не работает, что означает только уникальные символы оставаться.

; просто отделяет команды.

1234
1
123
12

поддерживает порядок, а также.


он не получает вещи в исходном порядке, но этот awk one-liner, кажется, работает:

awk '{for(i=1;i<=length();i++){a[substr(,i,1)]=1} for(i in a){printf("%s",i)} print "";delete a}' input.txt

разделить на части для облегчения чтения, это может быть автономным, как это:

#!/usr/bin/awk -f

{
  # Step through the line, assigning each character as a key.
  # Repeated keys overwrite each other.
  for(i=1;i<=length();i++) {
    a[substr(,i,1)]=1;
  }

  # Print items in the array.
  for(i in a) {
    printf("%s",i);
  }

  # Print a newline after we've gone through our items.
  print "";

  # Get ready for the next line.
  delete a;
}

конечно, та же концепция может быть реализована довольно легко и в pure bash:

#!/usr/bin/env bash

while read s; do
  declare -A a
  while [ -n "$s" ]; do
    a[${s:0:1}]=1
    s=${s:1}
  done
  printf "%s" "${!a[@]}"
  echo ""
  unset a
done < input.txt

обратите внимание, что это зависит от bash 4, из-за ассоциативного массива. А этот! .. --14-->тут получить вещи в исходном порядке, потому что bash делает лучшую работу держать ключи в порядке, чем на awk.

и я думаю, что у вас есть решение с помощью sed от Хосе, хотя у него есть куча дополнительных фитингов. :)

последний инструмент, который вы упомянули grep. Я уверен, что вы не можете сделать это в традиционном grep, но, возможно, какая-то храбрая душа сможет построить вариант perl-regexp (т. е. grep -P) через -o и lookarounds. Им нужно больше кофе, чем во мне сейчас.


один из способов, используя perl:

perl -F -lane 'print do { my %seen; grep { !$seen{$_}++ } @F }' file

результаты:

1234
1
123
12

другое решение,

while read line; do 
  grep -o . <<< $line | sort -u | paste -s -d '' -;
done < file

grep -o . преобразовать 'row line' в 'column line'
sort -u сортировка писем и удалить repetead буквы
paste -s -d '' - преобразовать 'column line' в 'row line'
- в качестве аргумента имени файла для вставки, чтобы сказать ему использовать стандартный ввод.


этот awk должен работать:

awk -F '' '{delete a; for(i=1; i<=NF; i++) a[$i]; for (j in a) printf "%s", j; print ""}' file
1234
1
123
12

здесь:

-F '' побьет рекорд char по char, давая нам один символ в , etc.

Примечание: для использования без gnu awk:

awk 'BEGIN{FS=""} {delete a; for(i=1; i<=NF; i++) a[$i]; 
        for (j in a) printf "%s", j; print ""}' file

Это может сработать для вас (GNU sed):

sed 's/\B/\n/g;s/.*/echo "&"|sort -u/e;s/\n//g' file

разбить каждую строку на ряд строк. Уникальная сортировка этих строк. Объедините результат в одну строку.


уникальная и отсортированная альтернатива другим, используя инструменты sed и gnu:

sed 's/\(.\)/\n/g' file | sort | uniq

который производит один символ на строку; если вы хотите, чтобы они были на одной строке, просто сделайте:

sed 's/\(.\)/\n/g' file | sort | uniq | sed ':a;N;$!ba;s/\n//g;'

это имеет то преимущество, что символы отображаются в отсортированном порядке, а не в порядке появления.