2015-07-02 7 views
0

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

У меня есть файл в следующем формате:

alpha • a | b | c | a | b | c | d 
beta • h | i | i | h | i | j | k 
gamma •  m | n | o 
delta • p | p | q | r | s | q 

Таким образом, есть заглавный в колонке 1, а затем различные слова с разделителями трубами, с непредсказуемым количеством дублирования. На желаемом выходе удалены одинарные символы:

alpha • a | b | c | d 
beta • h | i | j | k 
gamma •  m | n | o 
delta • p | q | r | s 

Мой входной файл - несколько тысяч строк. Греческие имена выше соответствуют названиям категорий (например, «бейсбол»); и алфавит соответствует английским словарным словам (которые могут содержать пробелы или акценты), например. «игра в мяч», «биттер», «ловец», «ловец», назначенный нападающий ».

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

Bonus сложность, я часто комментарий в конце, который должен быть сохранен, например,

zeta • x | y | x | z | z ; comment here 

P.S. этот вход фактически выход предварительного StackOverflow вопрос: Command line to match lines with matching first field (sed, awk, etc.)

+0

Итак, у вас есть три разделителя, средняя точка или пуля, трубы и (иногда) точки с запятой. Имеются ли эти символы когда-либо, кроме как разделителей? Важно ли, чтобы имена были в алфавитном порядке после того, как они были уникальными? –

+0

Мой пример сортируется, но реальный ввод не сортируется. Эти три разделителя (•, | ,;) ТОЛЬКО появляются в полевых разделителях. Порядок вывода является гибким (может быть таким же, как вход или сортировка). –

ответ

1

BSD awk не имеет sort функции встроенные, где GNU awk делает, но я не уверен, что они необходимы. Пуля, • (U + 2022), вызывает некоторое горе с awk.

Предлагаю предварительно обработать пулю однобайтовым символом. Я выбрал @, но вы можете использовать Control-A или что-то еще, если хотите. Ваши данные были в файле data. Я отмечаю, что перед m было двойное пространство в линии gamma; Я предполагаю, что это не имеет значения.

sed 's/•/@/' data | 
awk -F ' *[@|] *' ' 
{ 
    delete names 
    delete comments 
    delete fields; 
    if ($NF ~/*;/) { split($NF, comments,/*; */); $NF=comments[1]; } 
    j = 1; 
    for (i = 2; i <= NF; i++) 
    { 
     if (names[$i]++ == 0) 
      fields[j++] = $i; 
    } 
    printf("%s", $1); 
    delim = "•" 
    for (k = 1; k < j; k++) 
    { 
     printf(" %s %s", delim, fields[k]); 
     delim = "|"; 
    } 
    if (comments[2]) 
     printf(" ; %s", comments[2]); 
    printf("\n"); 
}' 

Идущие это дает:

alpha • a | b | c | d 
beta • h | i | j | k 
gamma • m | n | o 
delta • p | q | r | s 
zeta • x | y | z ; comment here 
+0

Появляется, чтобы работать отлично, спасибо! Я изменил свой первый разделитель на «@», не указав необходимость в sed. Возможно, это не один лайнер, но он чист и лучше, чем более сложная небольшая программа, которую я имел в виду. –

1

С Баш, сортировать, xargs, СЭД:

while IFS='•;' read -r a b c; do 
    IFS="|" read -ra array <<< "$b" 
    array=("${array[@]# }") 
    array=("${array[@]% }") 
    readarray -t array < <(printf '%s\0' "${array[@]}" | sort -zu | xargs -0n1) 
    SAVE_IFS="$IFS"; IFS="|" 
    s="$a• ${array[*]}" 
    [[ $c != "" ]] && s="$s ;$c" 
    sed 's/|/ | /g' <<< "$s" 
    IFS="$SAVE_IFS" 
done < file 

Выход:

 
alpha • a | b | c | d 
beta • h | i | j | k 
gamma • m | n | o 
delta • p | q | r | s 
zeta • x | y | z ; comment here 

Я полагаю, что два пространства, прежде чем " m "- опечатка.

+0

да, 2 пробела до «м» - это опечатка, и я мог бы очистить свой контент, чтобы точно соответствовать моей демонстрации. –

+0

Спасибо, что написал это. Я всегда немного боюсь прямых скриптов bash, если что-то не так с моим вводом, и я случайно rm -r /. Я не тестировал это, так как ответ от Джонатана работает, но спасибо. –

1

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

sed 'h;s/.*• \([^;]*\).*/cat <<\\! | sort -u |\1|!/;s/\s*|\s*/\n/2ge;s/\n/ | /g;G;s/^\(.*\)\n\(.*• \)[^;]*/\2\1/;s/;/ &/' file 

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

Здесь копия строки хранится в удержании. Идентификатор и комментарии удалены.Данные заносятся в файл с помощью cat и синтаксиса bash here-document и передаются через сортировку (и uniq, если ваш сорт не оснащен опцией -u). Пространство шаблона оценивается, и строка повторно собрана путем добавления исходной строки к пространству шаблона и использования соответствия шаблону регулярного выражения.

Смежные вопросы