Проблема:
Проблема здесь (как предложил @Jens в комментарии) связана с использованием опций -e
и pipefail
; -e
немедленно завершает работу оболочки, если какая-либо простая команда получает ошибку, а pipefail
вызывает сбой конвейера, если какая-либо команда в ней не срабатывает.
Но что не так? Взгляните на команду здесь:
start_read=$(cat $READS | grep 'read' | head -n $(($Ndiv*${i}+1)) | tail -n 1)
, которая явно выполняет команды cat
, grep
, head
и tail
в конвейере (который выполняется в подоболочке так, вывод может быть захвачен и помещен в переменную start_read
). Итак, cat
запускается и начинает читать из файла и пихать его по трубе в grep
. grep
читает это, выбирает строки, содержащие «read», и подает их к head
. head
читает первую строку этого (обратите внимание, что на первом проходе Ndiv
равен 0, поэтому он запускает head -n 1
) со своего входа, передает его на команду tail
, и затем выходит . tail
проходит по одной полученной строке, а затем также выходит.
Проблема в том, что при выходе из head
он не прочитал все, что grep
должен был дать; из-за этого grep
пытался вытолкнуть данные в канал без чего-либо на другом конце, поэтому система отправила ему сигнал SIGPIPE, чтобы сообщить, что он не будет работать, и это вызвало grep
для выхода с состоянием ошибки , И затем, с момента выхода, cat
аналогичным образом пытался заполнить осиротевшую трубу, поэтому он также получил SIGPIPE и также завершился со статусом ошибки.
Так как cat
и grep
вышли с ошибки, и pipefail
установлено, что подоболочка также завершится со статусом ошибки, и это означает, что родительская оболочка считает всю команду назначения не выполненной, и немедленно прервет выполнение сценария.
Решения:
Итак, одним из возможных решений является удаление опции -e
из команды set
. -e
является своего рода шумихой в том, что она считает ошибкой, достойной выхода, а в чем - нет, поэтому, как правило, она мне вообще не нравится (подробности см. BashFAQ # 105 ).
Другая проблема с -e
заключается в том, что (как мы видели здесь) он не дает никаких признаков того, что пошло не так, или даже , что что-то пошло не так! Проверка ошибок важна, но и ошибка , сообщающая .
(Примечание: опасность при удалении -e
заключается в том, что ваш скрипт может получить серьезную ошибку на полпути ... и затем слепо хранить выполнение в ситуации, которая не имеет смысла, возможно, повреждает вещи в процессе. Поэтому вы должны подумать о том, что может go быть неправильным во время выполнения скрипта, и добавить ручную проверку ошибок по мере необходимости. Я добавлю несколько примеров к мое предложение сценария ниже.)
В любом случае, просто удаление -e
означает просто то, что это не очень хороший подход к проблеме. Вы читаете (или пытаетесь прочитать) весь файл несколько раз и каждый раз обрабатываете его с помощью нескольких команд. Вы действительно должны только прочитать вещь дважды: один раз, чтобы выяснить, сколько read
s, и один раз, чтобы разбить его на куски. Возможно, вы сможете написать программу для разбиения на awk
, но большинство unix -подобных систем уже имеют программу специально для этой задачи: split
. Также нет необходимости в cat
везде, так как другие команды вполне способны читать непосредственно из файлов (опять же, @Jens указал на это в комментарии).
Так что я думаю, что-то вроде этого будет работать:
#!/bin/bash
set -uo pipefail # I removed the -e 'cause I don't trust it
nodes=8 # Note: lower- or mixed-case variables are safer to avoid conflicts
reads=0days_rep2.fasta
splitprefix=0days_split_
Ntot=$(grep -c 'read' "$reads") || { # grep can both read & count in a single step
# The || means this'll run if there was an error in that command.
# A normal thing to do is print an error message to stderr
# (with >&2), then exit the script with a nonzero (error) status
echo "$0: Error counting reads in $reads" >&2
exit 1
}
Ndiv=$((($Ntot+$nodes-1)/$nodes)) # Force it to round *up*, not down
grep 'read' "$reads" | split -l $Ndiv -a1 - "$splitprefix" || {
echo "$0: Error splitting fasta file" >&2
exit 1
}
Это создаст файлы с именами от «0days_split_a» до «0days_split_h». Если у вас есть версия GNU split
, вы можете добавить ее опцию -d
(используйте суффиксы цифры c вместо букв) и / или --additional-suffix=.fasta
(чтобы добавить расширение .fasta к разделенным файлам).
Еще одно замечание: если только небольшой размер этого большого файла составляет read
строк, возможно, сначала будет быстрее выполнить grep 'read' "$reads" >sometempfile
, а затем запустить оставшуюся часть сценария для временного файла, поэтому не нужно читать и разбавлять его дважды. Но если большая часть файла read
строк, это не сильно поможет.