0

I wrote a simple piece of code:

import subprocess
p=subprocess.Popen('mkdir -p ./{a,b,c}', shell=True, stderr=subprocess.STDOUT)
p.wait()

Unfortunately, it not always behaves the way I'd expect. I.e, when I run it on my PC, everything is OK (ls -l gives me three dirs: a, b and c). But when my colleague runs it on his desktop, he gets... one dir named: '{a,b,c}' ... We both use Python 2.7.3. Why is that? How would you fix it?

I tried to find the answer by myself. According to manual: "args should be a sequence of program arguments or else a single string. By default, the program to execute is the first item in args if args is a sequence. If args is a string, the interpretation is platform-dependent and described below. See the shell and executable arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass args as a sequence."

So I tried to execute the code in shell:

python -c "import subprocess; p=subprocess.Popen(['mkdir', '-p', './{ea,fa,ga}'], shell=True, stderr=subprocess.STDOUT); p.wait()"

And I got:

mkdir: missing operand

I will be thankful for any advice

Thanks!

user1703589
  • 41
  • 1
  • 5

4 Answers4

2

The ./{a,b,c} syntax is bash syntax, not supported by all shells.

The documentation says:

On Unix with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell.

So your command only works if /bin/sh is symlinked to a shell that supports that syntax, like bash or zsh. Your colleague is probably using dash or another shell that doesn't support this.

You should no be relying in something like a user's default shell. Instead, write the full command with the full expansion:

p = subprocess.Popen('mkdir -p ./a ./b ./c', shell=True, stderr=subprocess.STDOUT)
Darkhogg
  • 13,707
  • 4
  • 22
  • 24
0

There's several problems here.

  • First: if you are using a sequence of arguments, do not set "shell = True" (this is recommended in the Popen manual). Set it to False, and you'll see that your mkdir command will be accepted.
  • "./{a,b,c}" is AFAIK a specific syntax in bash. If your colleague is using a different shell, it will probably not work, or behave differently.
  • You should use the python "mkdir" command instead of calling a shell command, it will work whatever the server / shell / OS.
sterfield
  • 71
  • 4
0

Thank you all for your answers. It seems, that the best way is simply use /bin/sh syntax. I changed my code to use:

'mkdir -p ./a ./b ./c'

as you suggested.

I avoided to use mkdir() function, because I am writing a scripts with plenty of system calls, and I wanted to provide elegant --dry-run option (so I could list all of the commands).

Problem solved - thank you!

user1703589
  • 41
  • 1
  • 5
-1

The os.mkdir(path,[mode]) method are as far as I understand safer to use when working on multiplatform projects.

os.mkdir(os.getcwd()/a)

However its not as elegant as taking the subprocess approach.

petny
  • 21
  • 3
  • Your code doesn't work, you need to use quotes: `os.getcwd() + "/a"`. This also don't answer the specific OP's question. – Darkhogg May 19 '14 at 10:40