переменная подмножества путем сопоставления строк в одном столбце со строками в другой переменной - PullRequest
1 голос
/ 08 апреля 2020

У меня есть список строк (составных имен) в variable_1. Я использую bash и пытаюсь установить переменную_2, выбрав строки, чьи строки в столбце 2 точно соответствуют строкам в переменной_1. Результат будет содержать значения из столбцов 1 и 2, как показано в переменной 3 ниже. У меня есть много тысяч строк, но пример данных показан ниже.

Любые bash, grep, awk et c решения? Я не могу понять это.

Заранее спасибо.

echo "$ variable_1":

Zeaxanthin
Zeaxanthin diglucoside
Zentinic
Zephyramine
(Z)-Phenylacetaldehyde oxime
Zymosterol
Zymosterone

echo "$ variable_2":

C00371 Zeatin
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C15984 Zeaxanthin diglucoside diester
C08590 Zeinoxanthin
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone

echo "$ variable_3" (результат)

C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone

Ответы [ 3 ]

1 голос
/ 08 апреля 2020

Я представляю одно краткое, не слишком портативное решение:

#!/bin/bash
gawk '
    NR==FNR{
        a[$0]++;next
    }
    {
        x=gensub(/^[^ ]* /,"",1)
        if (x in a) print
    }
' <(echo "$variable_1") <(echo "$variable_2")

И еще немного дольше, но совместимое с POSIX:

#!/bin/sh
echo "$variable_1" > file1
echo "$variable_2" > file2

awk '
    NR==FNR{
        a[$0]++;next
    }
    {
        x=$0
        sub(/^[^ ]* /,"",x)
        if (x in a) print
    }
' file1 file2

В качестве последнего замечания я настоятельно рекомендую вам не собирать кучу строк в переменную. Это только усложняет жизнь.

0 голосов
/ 08 апреля 2020

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

Важное наблюдение: всякий раз, когда мы говорим о grep, sed и awk, мы всегда говорим о регулярных выражениях. Мы должны обратить внимание на значение специальных символов (возможных метасимволов) во входных данных. Например, один из ваших входов использует скобки. К счастью, это не метасимволы в BRE (основные выражения c), поэтому мне не нужно было быть слишком осторожным; но если, например, ваши входные строки могут содержать точку или звездочку и т. д. c., вам придется обратить внимание на эту проблему, иначе вы получите неправильные результаты.

Итак, вот два файла (для удобства чтения добавлены пустые строки):

[mathguy@localhost ~/test]$ more f1

Zeaxanthin
Zeaxanthin diglucoside
Zentinic
Zephyramine
(Z)-Phenylacetaldehyde oxime
Zymosterol
Zymosterone

[mathguy@localhost ~/test]$ more f2

C00371 Zeatin
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C15984 Zeaxanthin diglucoside diester
C08590 Zeinoxanthin
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone

И вот возможное решение:

[mathguy@localhost ~/test]$ sed 's/^/[^ ]* /' f1 | grep -x -f - f2

C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone

Это предполагает, что строка во втором файле должна состоять из нуля или несколько непробельных символов, за которыми следует ровно один пробел, а затем полная строка из первого файла sed берет каждую строку из первого файла и добавляет символы [^ ]* (обратите внимание, что после звездочки есть пробел, который не так легко увидеть невооруженным глазом). Вывод sed используется как "файл" регулярных выражений в функции grep (вызывается через -). grep использует флаг -x, чтобы требовать точного соответствия всей строки текста.

РЕДАКТИРОВАТЬ ОП в комментарии (ниже) заявил, что некоторые из входных строк делают фактически содержит символы, которые являются метасимволами для BRE. Вот как это можно сделать. Первый проход через sed просто добавляет обратную косую черту sh перед каждым метасимволом в строках из f1; тогда все остальное не изменится.

sed 's/[\.*^$[]/\\&/g' f1 | sed 's/^/[^ ]* /' | grep -x -f - f2

Надеюсь, я правильно помню метасимволы BRE (обратная косая черта sh, точка, звездочка, каретка, доллар и квадратная скобка с открытием). В противном случае список в выражении в скобках в первом sed следует изменить по необходимости.

0 голосов
/ 08 апреля 2020

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

  • может объединяться только в одном поле
  • входные данные должны быть отсортированы по полю соединения

Мы можем обойти это с помощью нескольких наборов преобразований ...

Начнем с рассмотрения того, как это будет сделано, если данные находятся в 2 файлах:

$ cat f1
Zeaxanthin
Zeaxanthin diglucoside
Zentinic
Zephyramine
(Z)-Phenylacetaldehyde oxime
Zymosterol
Zymosterone

$ cat f2
C00371 Zeatin
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C15984 Zeaxanthin diglucoside diester
C08590 Zeinoxanthin
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone

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

В этом примере мы будем использовать запятую (,); для файла f1 мы введем запятую в качестве ведущего символа, а для файла f2 мы заменим первый пробел на запятую:

$ sed 's/^/,/' f1
,Zeaxanthin
,Zeaxanthin diglucoside
,Zentinic
,Zephyramine
,(Z)-Phenylacetaldehyde oxime
,Zymosterol
,Zymosterone

$ sed 's/ /,/' f2
C00371,Zeatin
C06098,Zeaxanthin
C15969,Zeaxanthin diglucoside
C15984,Zeaxanthin diglucoside diester
C08590,Zeinoxanthin
C16075,(Z)-Phenylacetaldehyde oxime
C05437,Zymosterol
C22136,Zymosterone

В этот момент оба набора данных теперь имеют 2 поля (на основе разделителя запятой). Да, поле № 1 для файла f1 является нулевым.

С точки зрения join мы планируем объединить 2 набора данных на основе поля № 2, но сначала нам нужно получить данные, отсортированные по поле № 2 ... что мы можем сделать с помощью команды sort.

В обоих случаях мы отсортируем наборы данных по полю № 2 (используя запятую в качестве разделителя):

$ sed 's/^/,/' f1 | sort -t, -k2
,(Z)-Phenylacetaldehyde oxime
,Zeaxanthin
,Zeaxanthin diglucoside
,Zentinic
,Zephyramine
,Zymosterol
,Zymosterone

$ sed 's/ /,/' f2 | sort -t, -k2
C16075,(Z)-Phenylacetaldehyde oxime
C00371,Zeatin
C06098,Zeaxanthin
C15969,Zeaxanthin diglucoside
C15984,Zeaxanthin diglucoside diester
C08590,Zeinoxanthin
C05437,Zymosterol
C22136,Zymosterone

Теперь мы готовы передать эти наборы данных в join:

$ join -j2 -t, -o1.1,1.2 <(sed 's/ /,/' f2 | sort -t, -k2) <(sed 's/^/,/' f1 | sort -t, -k2) 
C16075,(Z)-Phenylacetaldehyde oxime
C06098,Zeaxanthin
C15969,Zeaxanthin diglucoside
C05437,Zymosterol
C22136,Zymosterone

ПРИМЕЧАНИЕ: -o1.1,1.2 говорит, что отображать только поля № 1 и № 2 из первого набора данных .

Все, что нужно на этом этапе - это снять нашу запятую; мы можем сделать это с помощью другой команды sed:

$ join -j2 -t, -o1.1,1.2 <(sed 's/ /,/' f2 | sort -t, -k2) <(sed 's/^/,/' f1 | sort -t, -k2) | sed 's/,/ /'g
C16075 (Z)-Phenylacetaldehyde oxime
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C05437 Zymosterol
C22136 Zymosterone

Теперь, чтобы обратиться к тому же набору данных, но хранящемуся в 2x переменных ...

$ echo "
++++++++++++++++++++
${variable_1}
++++++++++++++++++++
${variable_2}
++++++++++++++++++++
"
++++++++++++++++++++
Zeaxanthin
Zeaxanthin diglucoside
Zentinic
Zephyramine
(Z)-Phenylacetaldehyde oxime
Zymosterol
Zymosterone
++++++++++++++++++++
C00371 Zeatin
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C15984 Zeaxanthin diglucoside diester
C08590 Zeinoxanthin
C16075 (Z)-Phenylacetaldehyde oxime
C05437 Zymosterol
C22136 Zymosterone
++++++++++++++++++++

This должно быть (относительно) легко, потому что нам просто нужно заменить ссылки на наши файлы нашими переменными, и для этого примера мы передадим переменные начальным командам sed в виде следующих строк:

$ join -j2 -t, -o1.1,1.2 <(sed 's/ /,/' <<< "${variable_2}" | sort -t, -k2) <(sed 's/^/,/' <<< "${variable_1}" | sort -t, -k2) | sed 's/,/ /'
C16075 (Z)-Phenylacetaldehyde oxime
C06098 Zeaxanthin
C15969 Zeaxanthin diglucoside
C05437 Zymosterol
C22136 Zymosterone
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...