The major difference is the way you construct your commands:
subprocess.Popen("ls -l /tmp", shell=True)
The first example is one string and works with shell set to true, the second example is without. This requires each part of the command to be passed as a seperate item in the array. The danger is when you accept part of the command as input from an untrusted source.
For instance if you would accept part of the command from a user, in the first example he can literally type any command he wants, whereas in the latter example he can only define on parameter or destination. Adding more commands will not be possible as subprocess will fail (providing you do not construct the array based on the user input, but just accept one argument for a specific position).