如果管道的左侧失败,则从 subprocess.Popen 启动一个 shell 管道失败

时间:2021-07-02 15:55:32

标签: linux bash shell subprocess

我在 python 中使用 subprocess.popen 运行 bash 命令:

cmd = "bwa-mem2/bwa-mem2 mem -R \'@RG\\tID:2064-01\\tSM:2064-01\\tLB:2064-01\\tPL:ILLUMINA\\tPU:2064-01\' reference_genome/human_g1k_v37.fasta BHYHT7CCXY.RJ-1967-987-02.2_1.fastq BHYHT7CCXY.RJ-1967-987-02.2_2.fastq -t 14 | samtools view -bS -o dna_seq/aligned/2064-01/2064-01.6.bam -"

process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)

问题是,即使第一个命令失败,我也会得到返回码 0。 我在谷歌上搜索并发现了 pipefail,这似乎是我应该使用的。

但是,我不明白在哪里写。我试过了:

"set -o pipefail && bwa-mem2/bwa-mem2 mem -R \'@RG\\tID:2064-01\\tSM:2064-01\\tLB:2064-01\\tPL:ILLUMINA\\tPU:2064-01\' reference_genome/human_g1k_v37.fasta BHYHT7CCXY.RJ-1967-987-02.2_1.fastq BHYHT7CCXY.RJ-1967-987-02.2_2.fastq -t 14 | samtools view -bS -o dna_seq/aligned/2064-01/2064-01.6.bam -"

其中给出:/bin/sh: 1: set: Illegal option -o pipefail

有什么想法我应该如何合并这个吗?

编辑:

我不确定在回复答案时编辑我的答案是否正确?没有足够的字符在评论中回复:/

无论如何, 我在没有 shell=True @Charles Duffy 的情况下尝试了你的第二种方法。

(cmd_1 和 cmd_2 与您在解决方案中编写的内容相同)

这是我使用的代码:

try:
        
   p1 = Popen(shlex.split(cmd_1), stdout=PIPE)
   p2 = Popen(shlex.split(cmd_2), stdin=p1.stdout, stdout=PIPE, stderr=STDOUT, text=True)
   p1.stdout.close()
   output, error = p2.communicate()
   p1.wait()    
    
   rc_1 = p1.poll()
   rc_2 = p2.poll()
   print("rc_1:", rc_1)
   print("rc_2:", rc_2)

   if rc_1 == 0 and rc_2 == 0:
       self.log_to_file("DEBUG", "# Process ended with returncode = 0")
       if text: self.log_to_file("INFO", f"{text} succesfully 
            
   else:
       print("Raise exception")
       raise Exception(f"stdout: {output} stderr: {error}")

except Exception as e:
    print(f"Error: {e} in misc.run_command()")
    self.log_to_file("ERROR", f"# Process ended with returncode != 0, {e}")

这是我通过重命名一个文件故意导致错误时得到的结果:

[E::main_mem] failed to open file `/home/jonas/BASE/dna_seq/reads/2064-01/test_BHYHT7CCXY.RJ-1967-987-02.2_2.fastq.gz'.
free(): double free detected in tcache 2
rc_1: -6
rc_2: 0
Raise exception
Error: stdout:  stderr: None in misc.run_command()
 ERROR: # Process ended with returncode != 0, stdout:  stderr: None

它似乎捕获了错误的返回码。

但为什么 stdout 为空而 stderr= None 为空? 如何在进程成功和失败时捕获输出以将其记录到记录器中?

1 个答案:

答案 0 :(得分:4)

首先,有壳

不要让 shell=True 默认指定 sh,而是明确指定 bash 以确保 pipefail 是一个可用功能:

shell_script = r'''
set -o pipefail || exit
bwa-mem2/bwa-mem2 mem \
  -R '@RG\tID:2064-01\tSM:2064-01\tLB:2064-01\tPL:ILLUMINA\tPU:2064-01' \
  reference_genome/human_g1k_v37.fasta \
  BHYHT7CCXY.RJ-1967-987-02.2_1.fastq \
  BHYHT7CCXY.RJ-1967-987-02.2_2.fastq \
  -t 14 \
  | samtools view -bS \
    -o dna_seq/aligned/2064-01/2064-01.6.bam -
'''

process = subprocess.Popen(["bash", "-c", shell_script],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           text=True)

有效,但它不是最好的选择。


第二,完全没有外壳

p1 = subprocess.Popen(
  ['bwa-mem2/bwa-mem2', 'mem',
   '-R', r'@RG\tID:2064-01\tSM:2064-01\tLB:2064-01\tPL:ILLUMINA\tPU:2064-01',
   'reference_genome/human_g1k_v37.fasta',
   'BHYHT7CCXY.RJ-1967-987-02.2_1.fastq',
   'BHYHT7CCXY.RJ-1967-987-02.2_2.fastq', '-t', '14'],
  stdout=subprocess.PIPE)
p2 = subprocess.Popen(
  ['samtools', 'view', '-bS',
   '-o', 'dna_seq/aligned/2064-01/2064-01.6.bam', '-'],
  stdin=p1.stdout,
  stdout=subprocess.PIPE,
  stderr=subprocess.PIPE,
  text=True)
p1.stdout.close()
output, _ = p2.communicate() # let p2 finish running
p1.wait()                    # ensure p1 has properly exited

print(f'bwa-mem2 exited with status {p1.returncode}')
print(f'samtools exited with status {p2.returncode}')

...可让您分别检查 p1.returncodep2.returncode