Сравнение индексов двух строк

У меня две строки с двойными кавычками одинаковой длины, которые выглядят так:

"$con" – (состоит из пробелов и * s):

  * ****** *** ** * ** 

и "$prot" – (состоит из букв и -s):

 M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ 

Как извлечь строку букв в одной позиции в $prot соответствующую позиции * 's в $con .

5 Solutions collect form web for “Сравнение индексов двух строк”

Если ваши строки не содержат новой строки, вот решение, использующее только инструменты POSIX. Он работает, помещая все символы в отдельную строку, сжимая два файла вместе и извлекая строки, где первый файл (теперь первый столбец) имеет * .

 con_file=$(mktemp) echo "$con" | sed -e 's/./&\ /g' >"$con_file" prot_lines=$(echo "$prot" | sed -e 's/./&\ /g') prot_extract=$(echo "$prot_lines" | paste -d "$con_file" - | sed -n 's/^* //p' | tr -d '\n') 

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

 echo "$prot" | awk -v filter="$con" '{ for (i=1; i<=length; i++) { if (substr(filter, i, 1) == "*") printf "%c", substr($0, i, 1); } } END {printf "\n"}' 

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

 indices=$(echo "$con" | sed 's/\*\**/,&\n/g' | awk -F , 'BEGIN {start = 1} /\*/ {start += length($1); printf "%d-", start; start += length($2); printf "%d,", start - 1}') indices=${indices%,} echo "$prot" | cut "$indices" 

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

 #!/usr/bin/perl $con =' * ****** *** ** * ** '; $prot ='M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ'; @c_con = split(//, $con); @c_prot = split(//, $prot); @i_con = grep { $c_con[$_] eq '*' } 0 .. $#c_con; map { print "index: $_, value: @c_prot[$_]\n" } @i_con; 

пример

Запустив его, вы получите список индексов и значений в этих индексах в $prot .

 $ ./extracvals.pl index: 24, value: R index: 28, value: Y index: 29, value: Y index: 30, value: H index: 31, value: Q index: 32, value: F index: 33, value: L index: 41, value: M index: 42, value: K index: 43, value: A index: 45, value: K index: 46, value: A index: 48, value: W index: 67, value: G index: 68, value: L 

Как это работает?

Сценарий создает 2 строки, как описано OP, $con и $prot . Затем эти строки считываются в 2 массива, так что каждый символ в строке занимает ячейку в массиве. Это делается с использованием функции split Perl:

 @c_con = split(//, $con); @c_prot = split(//, $prot); 

2 новых массива, @c_con (содержит $con ) и @c_prot (содержит $prot ).

Затем мы используем функцию grep Perl, чтобы найти все индексы в массиве @c_con которые имеют значение '*' . Этот список индексов сохраняется в другом массиве @i_con .

Наконец, мы используем функцию map Perl для печати значения индекса и соответствующего значения в массиве @c_prot при заданном индексе. Функция map принимает каждое значение из массива @i_con и оценивает команду в фигурных скобках:

 { print "index: $_, value: @c_prot[$_]\n" } 

для каждого из этих значений. Индекс хранится во временной переменной Perl, $_ , поскольку мы map итерации через массив @i_con .

Я работал с аналогичным решением для slm (он избил меня до этого!) Но если OP хочет избежать кодирования, все это в perl:

 #!/bin/sh con=" * ****** *** ** * **" prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ" # put the con and prot variables into our environment variables export con prot # then call perl result=$(perl -e 'my @x = split(//, "$ENV{con}"); my @i = grep { $x[$_] eq "*" } 0 .. $#x; print join("", map { substr("$ENV{prot}", $_, 1) } @i );' ) # now we have your answer in $result echo "$result" # then once finished with con and prot, unset them from the environment unset con prot 

$ENV – это зарезервированная переменная в perl, содержащая хэш всех имен и значений переменных среды , поэтому получение значения $ prot (которое мы использовали для export для его установки) можно просто получить через $ENV{prot} .

Вместо разделения массивов, подобных slm, я использовал substr который возвращает подстроку заданной строки (первый аргумент), при смещении (2-й аргумент) заданной длины (третий аргумент). Кроме того, мы, похоже, использовали идентичные решения ( довольно жуткий, музыкальные песни Twilight Zone ) 🙂

Если мы сможем использовать gnu grep:

 cut -c$(grep -Fbo '*' <<<_"$con" | cut -f1 -d: | paste -sd,) <<<"$prot" 

Я уверен, что можно играть в гольф больше, но он отлично работает. -bo вызывает grep для вывода (b) yte-offset с (o) nly-matches (не целыми строками), что в качестве побочного эффекта приводит к тому, что grep находит все совпадения, а не только одну строку. Затем мы обрабатываем это в список чисел, разделенный запятыми, который мы можем подавать в качестве параметра командной строки для cut . Досадная деталь заключается в том, что grep считает, что индексы персонажей основаны на 0, и разрез считает, что они основаны на 1, поэтому нам нужно перевести $con на один символ; следовательно, _ .

 $ con=" * ****** *** ** * **" $ prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ" $ printf "%s\n%s\n" "$con" "$prot" * ****** *** ** * ** M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ $ cut -c"$(grep -Fbo '*' <<<_"$con" | cut -f1 -d: | paste -sd,)" <<<"$prot" RYYHQFLMKAKAWGL 

Простое решение с использованием функций оболочки Bash (в этом вопросе есть метка a /bash ):

 con=" * ****** *** ** * **" prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ" i=0 # Iterate until the index is less than the con string length while [ $i -lt ${#con} ] do # Get the current element of the con "character array" c=${con:$i:1} if [[ $c == '*' ]] then # Get the corresponding element from the prot character array p=${prot:$i:1} echo $i, $c, $p fi i=$((i+1)) done 

Выход:

 24, *, R 28, *, Y 29, *, Y 30, *, H 31, *, Q 32, *, F 33, *, L 41, *, M 42, *, K 43, *, A 45, *, K 46, *, A 48, *, W 67, *, G 68, *, L 

Конечно, вы можете изменить echo инструкцию, чтобы контролировать, что печатается.

Источник: Bash: разделите строку на массив символов

  • Сценарий, использующий fifos, не генерирующий выход при обработке из stdin
  • Насколько надежны / переносимы Nix-встроенные / команды (echo, ps, sort, uniq) из Debian в другие дистрибутивы
  • Сценарий оболочки, написанный в другой оболочке, чем моя текущая оболочка
  • Как найти максимальный и минимальный размер файла вместе со своим путем в каталоге
  • Простые сценарии обертки, порождающие 100s процессов bash
  • Как найти файлы к моменту их имени?
  • Как запустить «find -exec <script> {} \;
  • Машиночитаемое количество обновлений? Альтернатива / usr / lib / update-notifier / apt-check?
  • Найти все папки в каталоге с тем же контентом
  • передавать файл в удаленный каталог только в том случае, если он не существует без замены
  • Есть ли заклинание командной строки для сохранения определенных столбцов по имени в CSV-файле?
  • Linux и Unix - лучшая ОС в мире.