Как удалить повторяющееся значение в текстовом файле с разделителями табуляции

set -o pipefail errexit действительно предотвращает выполнение последующих команд, но это вам не помогает, потому что вы не пытаетесь предотвратить выполнение следующей команды. В трубопроводе производитель | Команды потребителя , производителя и потребителя выполняются параллельно . Вы не можете предотвратить запуск потребителя в случае отказа производителя , потому что, если не считать случайного совпадения времени, он уже запустился.

Если возможны только две возможности: « потребитель преуспевает и производит непустой вывод» и « потребитель терпит неудачу и не производит никакого вывода», вы можете использовать ifne ] из moreutils Джои Хесса .

producer | ifne consumer

Я не думаю, что это сработает в вашем случае - совпадения строк могут не оказаться (ложноотрицательные, и вы получите устаревшие данные), соединение с базой данных может быть потеряно посередине (ложное срабатывание и получить усеченные данные).

Если вам нужно узнать, преуспел ли производитель, вам нужно дождаться его завершения, прежде чем запускать потребителя. А поскольку потребителя еще нет, нужно что-то сохранить вывод.

Если вывод не содержит нулевых байтов и не слишком велик, вы можете сохранить его в переменной оболочки.

output=$(producer); producer_status=$?
if [ $producer_status -ne 0 ]; then
  echo >&2 "Producer failed with status $producer_status"
  exit $producer_status
fi
printf '%s\n' "$output" | consumer

В ksh93, bash или zsh эта последняя строка может быть упрощена до consumer .

Обратите внимание, что подстановка команд удаляет завершающие символы новой строки. Если уместны завершающие пустые строки, обходной путь состоит в том, чтобы изменить первую строку на

output=$(producer; echo a); producer_status=$?; output=${output%?}

. Если вывод потенциально слишком велик или может содержать нулевые байты, сохраните его во временном файле.

5
27.09.2017, 01:33
3 ответа

Это больше похоже на код -решение проблемы гольфа/фрика:

xargs -L1 -I{} echo '; {}' <./test.txt | \
      xargs -n1 | \
      uniq | \
      xargs | \
      sed -e 's/; /\n/g' -e 's/ \+/\t/g'

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

Он также основан на предположении, что ваши данные не содержат символ ;.

2
27.01.2020, 20:31

замена sed обратной ссылкой

sed -re 's/\s+$//; s/(\t[^\t]+)\1+$/\1/'

(s/\s+$//избавляется от завершающего белого пробела -, как в вашем примере.)

0
27.01.2020, 20:31

Первый набор примерных данных:

$ awk -vOFS='\t' '{ r=""; delete t; for (i=1;i<=NF;++i) { if (!t[$i]++) { r = r ? r OFS $i : $i } } print r }' file
A       B1      C1
B       B2      D2
C       C12     C13
D       D3      D5      D9
G       F2

Второй набор примеров данных (тот же awkскрипт):

$ awk -vOFS='\t' '{ r=""; delete t; for (i=1;i<=NF;++i) { if (!t[$i]++) { r = r ? r OFS $i : $i } } print r }' file
A       CD274   PDCD1LG2        CD276
B       NEK2    NEK6    NEK10   NEKL-4
C       TNFAIP3 OTUD7B
D       DUSP16  DUSP4   DUSP8   VHP-1
E       AGO2

Сценарий считывает входной файл fileпострочно и для каждой строки проходит через каждое поле, формируя выходную строку, r. Если значение в поле уже было добавлено в строку вывода (, определяемую справочной таблицей t, используемых значений поля ), то поле игнорируется, в противном случае оно добавляется.

Когда все поля входной строки обработаны, выводится построенная строка.

Разделитель выходных полей установлен на табуляцию через -vOFS='\t'в командной строке.


Расшифровка сценария awk:

{
    r = ""
    delete t

    for (i = 1; i <= NF; ++i) {
        if (!t[$i]++) {
            r = r ? r OFS $i : $i
        }
    }

    print r
}
7
27.01.2020, 20:31

Теги

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