Использование переменных в конечной команде

#bash #tail

#bash #tail

Вопрос:

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

Например, файл ссылки выглядит как:

 ggaaatgcattcaaacatgc
  

И список выглядит так:

 5
10
7
15
  

Я пытался использовать этот код:

 list=$(<pos.txt)
echo "$list"
cat ref.txt | tail -c  "list" | head -c1 > out.txt
  

Однако он продолжает возвращать «недопустимое количество байтов: ‘ 5 n10 n7 n15 …'»

Мой ожидаемый результат был бы

 a
t
g
a
... 
  

Кто-нибудь может сказать мне, что я делаю не так? Спасибо!

Комментарии:

1. У меня такое чувство, что вы обрабатываете какую-то часть файла fasta, и проблема, которую вы публикуете, является XY-проблемой. Т. Е. вы задаете этот вопрос, чтобы решить другую проблему, которая могла бы быть решена более эффективно другим способом.

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

3. Этот подход не будет работать должным образом с файлами fasta, потому что head , tail и т.д. Все работают по позициям символов, а не по базовым позициям. То есть они не знают, что нужно пропускать заголовок, комментарии или даже перевод строки между строками. Вам действительно нужны инструменты, которые знают, как анализировать файлы fasta.

Ответ №1:

Похоже, вы пытаетесь получить доступ к своей list переменной в своей хвостовой команде. Вы можете получить к нему доступ следующим образом: $list вместо того, чтобы просто заключать его в кавычки.

Ваша логика ошибочна даже после исправления доступа к переменной. list Переменная включает в себя все строки вашего list.txt файла. Включая символ новой строки n , который невидим во многих пользовательских интерфейсах и программах, но, конечно, виден, когда вы вручную считываете отдельные байты. Вам нужно вводить строки одну за другой, чтобы она работала должным образом.

Кроме того, если эти числа не являются индексами с конца, вам нужно передать их в head вместо tail .

Если я правильно понял, что вы пытаетесь сделать, это должно сработать:

 while read line
do
  head -c $line ref.txt | tail -c 1 >> out.txt
done < pos.txt
  

Ответ №2:

Причина сбоя вашей команды проста. Переменная list содержит многострочную строку, сохраненную из pos.txt файлов, включая новые строки. Вы не можете передать не более одного целого значения для -c флага.

Ваши попытки можно довольно легко исправить, удалив вызовы cat и используя временную переменную для хранения содержимого файла

 while IFS= read -r lineNo; do
    tail -c "$lineNo" ref.txt | head -c1
done < pos.txt
  

Но тогда, если ваши намерения каждый раз выводить желаемый результат в новой строке, head не выводится таким образом. Он просто формирует строку atga для вашего ввода в одной строке, а не в нескольких строках с одним символом в каждой строке.

Как упоминает Гордон в одном из комментариев, для гораздо более эффективной обработки файлов FASTA вы могли бы просто использовать один вызов awk , хотя (пропуская несколько разветвлений на head / tail ). Предоставленные вами входные данные не содержат заголовков для пропуска, которые были бы простыми, поскольку

 awk ' FNR==NR{ n = split($0,arr,""); for(i=1;i<=n;i  ) hash[i] = arr[i] } 
      ( $0 in hash ){ print hash[$0] } ' ref.txt pos.txt
  

Ответ №3:

Вы могли бы использовать cut вместо tail :

 pos=$(<pos.txt)
cut -c ${pos//$'n'/,} --output-delimiter=$'n' ref.txt
  

Или просто awk:

 awk -F '' 'NR==FNR{c[$0];next} {for(i in c) print $i}' pos.txt ref.txt
  

оба дают:

 a
g
t
a