I have a bash script setup where I eval a variable called snake and exit if there was an error. Even though there is no error upon executing the snake command, the aws s3 commands below are not executed.
if I removed || echo "ERROR OCCURED, LOOK ABOVE. EXITING" ; exit 1, then the aws commands will execute.
I know there isn't an error upon eval $snake because echo "ERROR OCCURED, LOOK ABOVE. EXITING" isn't returned to standard out (it only is when there is actually an error).
I need the aws commands to execute after successfully running eval $snake, but I'm unsure of how to do this.
s3='ebio/'
# do not edit contents below unless needed
snake="snakemake --default-remote-provider S3 --default-remote-prefix '$s3' --use-conda --cores 32 --rerun-incomplete --printshellcmds"
read -p "Rewrite over samples.tsv, peak_norm.tsv, and treated_vs_untreated.tsv files? (y/n)" -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
# exit if snake fails
eval $snake || echo "ERROR OCCURED, LOOK ABOVE. EXITING" ; exit 1
# remove temp files
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*tmp"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*sam.parsed"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*sam.parsed.done"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*.sam.tmp.combined_w_uniquemap.rmDup.sam"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*.sam.tmp.combined_w_uniquemap.prermDup.sam"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*.adapterTrim.round2.rmRep.bam.tmp"
aws s3 rm --recursive s3://"$s3"rep_element_pipeline --exclude "*" --include "*.fastq.gz.mapped_vs_bt2_hg38.sam"
aws s3 rm --recursive s3://"$s3"data_analysis --exclude "*" --include "*.saturations_reads.txt"
fi
CodePudding user response:
The immediate problem here is that in foo || bar; baz, baz happens no matter what foo's exit status was. In that respect, a semicolon, as a command separator, is handled in just the same way as a newline. That can be fixed with explicit grouping, as in, foo || { bar; baz; } -- or even more explicitly, if foo; then bar; baz; fi
The relevant code would be better written as:
# multi-line form optional, but lets you add comments after each line
snake=(
snakemake
--default-remote-provider S3
--default-remote-prefix '$s3' # FIXME: Sure you don't want "$s3" instead?
--use-conda
--cores 32
--rerun-incomplete
--printshellcmds
)
"${snake[@]}" || { echo "ERROR OCCURRED, LOOK ABOVE, EXITING" >&2; exit 1; }
Storing your command in an array or a function rather than a string lets you avoid eval and the security problems it poses; both these techniques are taught in BashFAQ #50, and the problems with eval are covered in detail in BashFAQ #48.
Using curly braces to put the exit 1 inside the same group as the echo ensures that they happen together or not at all.
Note that '$s3' is correct only if you want the exact string $s3 to be passed as the default remote prefix; if you want the contents of the variable named s3 to be used for that, change it to "$s3".
