Search code examples
pythonsubprocessuser-inputqiime

User input in subprocess.call


I am writing a program to automate some qiime2 commands. I want to incorporate user input.

So far, I have:

# Items to import
import subprocess
from sys import argv

#Variables
format=argv[1]

# Import sequences for Phred33 format
if format=='Phred33':
    cmnd = 'qiime tools import --type SampleData[PairedEndSequencesWithQuality] --input-path manifest.csv --output-path paired-end-demux.qza --source-format PairedEndFastqManifestPhred33'
    print('executing {}'.format(cmnd))
    res = subprocess.call(cmnd, shell=True)
    print('command terminated with status', res)

# Import sequences for Phred64 format
if format=='Phred64':
    cmnd = 'qiime tools import --type SampleData[PairedEndSequencesWithQuality] --input-path manifest.csv --output-path paired-end-demux.qza --source-format PairedEndFastqManifestPhred64'
    print('executing {}'.format(cmnd))
    res = subprocess.call(cmnd, shell=True)
    print('command terminated with status', res)

This works fine since there's only two possible user inputs, but I'd rather not have the if statements down the line when there will be countless possible user inputs.

This would be better:

cmnd = 'qiime tools import --type SampleData[PairedEndSequencesWithQuality] --input-path manifest.csv --output-path paired-end-demux.qza --source-format PairedEndFastqManifest', format

But qiime2 gives me errors with this. Is there another way?

Thank you!


Solution

  • Don't use shell=True when the command you are executing is built from unsanitized user input. It can lead to the user being able to execute arbitrary commands, even if this is not wanted.

    Also, pass the command as a list to subprocess.call to avoid issues with quoting.

    cmnd = [
        'qiime', 'tools', 'import',
        '--type', 'SampleData[PairedEndSequencesWithQuality]',
        '--input-path', 'manifest.csv',
        '--output-path', 'paired-end-demux.qza',
        '--source-format', 'PairedEndFastq{}'.format(format)
    ]
    print('executing {}'.format(' '.join(cmnd)))
    res = subprocess.call(cmnd)
    

    References, related questions