BASH останавливается без ошибок, но работает, если скопировано в терминал - PullRequest
0 голосов
/ 08 апреля 2020

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

set -ueo pipefail

NODES=8
READS=0days_rep2.fasta

Ntot=$(cat $READS | grep 'read' | wc -l)
Ndiv=$(($Ntot/$NODES))


for i in $(seq 0 $NODES)
do

  echo $i

  start_read=$(cat $READS | grep 'read' | head -n $(($Ndiv*${i}+1)) | tail -n 1)

echo ${start_read}

  end_read=$(cat $READS | grep 'read' | head -n $(($Ndiv*${i}+$Ndiv)) | tail -n 1)

echo ${end_read}

done

Если я запускаю скрипт:

(base) [andrea@andrea-xps data]$ bash cluster.sh 

0
>baa12ba1-4dc2-4fae-a989-c5817d5e487a runid=314af0bb142c280148f1ff034cc5b458c7575ff1 sampleid=0days_rep2 read=280855 ch=289 start_time=2019-10-26T02:42:02Z
(base) [andrea@andrea-xps data]$ 

похоже, что он резко останавливается после команды "echo $ {start_read}" без каких-либо ошибок. Если я копирую и вставляю скрипт в терминал, он запускается без проблем. Я использую Манджаро linux.

Андреа

Ответы [ 2 ]

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

Проблема:

Проблема здесь (как предложил @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 строк, это не сильно поможет.

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

Хорошо, мы нашли нарушителя спокойствия: set -e в сочетании с set -o pipefail. Ответ Гордона Дэвиссона предоставляет все детали. Я даю этот ответ с единственной целью пожать положительные отзывы о моих усилиях по отладке в комментариях к вашему ответу: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...