Оболочка является отличным инструментом для управления другими инструментами, но она не особенно подходит для анализа содержимого файлов.
Распространенным условием является использование оболочки для запуска Awk над набором файлов и вместо этого выполнение логики обнаружения в Awk. (Другими популярными инструментами являются Python и Perl; возможно, я бы занялся этим на Python, если бы начал с нуля.)
Независимо от языка вашего скрипта, вы должны избегать дублирования кода ; рефакторинг, чтобы поместить параметры в переменные и затем запустить код с этими параметрами, или, возможно, переместить функциональность в функцию и затем вызвать ее с другими параметрами. Например,
scan () {
local f
for f in *.fasta; do
# Inefficient: refactor to do the grep only once, then decide whether you want to show the output or not
if grep -q "$1" "$f"; then
# Always, always use double quotes around file names
echo "$f"
grep "^>" "$f" | tr -d ">"
grep -v ">" "$f" | grep -aob "$1"
fi
done
}
case $motif in
1) scan "$SIM_Type_1";; # Notice typo in variable name
2) scan "$SIM_Type_2";; # Ditto
3) scan "$SIM_Type_3";; # Ditto
4) scan "$SIM_Type_4";; # Ditto
5) scan "$SIM_TYPE_5";; # Notice inconsistent variable name
alpha) scan "$SIM_Type_alpha";;
beta) scan "$SIM_Type_beta";;
esac
Вы объявляете переменные _*Type_*
(или иногда *_TYPE_*
- оболочка чувствительна к регистру, и вам, вероятно, следует использовать одинаковую заглавную букву для всех переменных, чтобы вам было проще) в качестве массивов, но затем Вы, очевидно, пытаетесь использовать их как обычные скаляры. Я могу только догадываться о том, что вы намерены для переменных на самом деле содержать; но я предполагаю, что вы хотите что-то вроде
# These are strings which contain regular expressions
x='[ACGHILMPSTVDEFKNQRWY]'
psi='[ILV]'
psi_1='[IV]'
alpha='[DE]'
# These are strings which contain sequences of the above regexes
# The ${variable} braces are not strictly necessary, but IMHO help legibility
SIM_Type_1="${psi}${psi}${x}${psi}"
SIM_Type_2="${psi}${x}${psi}${psi}"
SIM_Type_3="${psi}${psi}${psi}${psi}"
SIM_Type_4="${x}${psi}${x}${psi}"
SIM_TYPE_5="${psi}${alpha}${psi}${alpha}${psi}"
SIM_Type_alpha="${psi_1}${x}${psi_1}${psi}"
SIM_Type_beta="${psi_1}${psi_1}.${x}${psi}"
# You had an empty spot here ^ I guessed you want to permit any character?
Если вы действительно хотите, чтобы это были массивы, способ доступа к содержимому массива - "${array[@]}"
, но тогда это не приведет к тому, что мы можем напрямую передать grep
или Awk, поэтому я решил объявить их как строки содержащие регулярные выражения для мотивов.
Но, повторюсь, Awk, вероятно, является лучшим языком для этого, поэтому давайте изменим scan
на сценарий Awk.
# This replaces the function definition above
scan () {
awk -v re="$1" '{ if (/^>/) label=$0
else if (idx = match($0, re, result) {
if (! printed) { print FILENAME; printed = 1 }
print len + idx ":" result[0]
}
len += 1+length($0) # one for the newline
}
# Reset printed if we skip to a new file
FNR == 1 { printed = 0 }' *.fasta
}
Основным осложнением здесь является переопределение вычисления смещения grep -b
байтов. Если это не является строго необходимым (возможно, достаточно номера строки?), Тогда сценарий Awk можно сократить до несколько более тривиального.
Использование grep -a
предполагает, что, возможно, ваши входные файлы содержат символы новой строки DOS. Я думаю, что это будет нормально работать независимо от этого.
Непосредственным преимуществом этого рефакторинга является то, что мы избегаем сканирования потенциально большого входного файла дважды. Мы сканируем файл только один раз и печатаем имя файла при первом совпадении.
Если вы хотите сделать сценарий более универсальным, это, вероятно, также лучшая отправная точка, чем решение grep | tr
, которое вы использовали ранее. Но если сценарий делает то, что вам нужно, и совпадения часто находятся в начале входного файла, или входные файлы не большие, возможно, вам вообще не захочется переключаться на Awk.
Также обратите внимание, что, как и в вашей логике grep
, это не будет работать, если последовательность разбита на несколько строк в файле FASTA, и совпадение окажется между одним из разрывов строк.
Наконец, запрос сценария для интерактивного ввода - это дизайн. Я бы посоветовал вам принять выбор пользователя в качестве аргумента командной строки.
motif=$1
Таким образом, вы использовали бы это как ./scriptname alpha
для запуска регулярного выражения alpha
против ваших файлов FASTA.
Другим возможным рефакторингом было бы прочитать все регулярные выражения мотива в несколько более сложный скрипт Awk и распечатать все совпадения для всех них в форме, которая затем позволит вам легко выбрать те, которые вы на самом деле хотите изучить, более подробно. Если у вас есть много данных для обработки, циклическое повторение всего один раз - огромный выигрыш.