-1

I am running cat command to read the Linux version using subprocess.run(). However it doesn't work, the error is: cat: '/etc/*-release': No such file or directory, and I can not use shell=True due to security. Any hints how to solve this is appreciated. Here is my code:

try:
    result = subprocess.run(
        shlex.split("cat /etc/*-release"),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True)
except subprocess.TimeoutExpired as err:
    result = err
Payam Mesgari
  • 953
  • 1
  • 19
  • 38

2 Answers2

2

That's the role of the shell to evaluate the *. If you don't use it, you need to do it yourself, glob can help you for that.

So you can fix your example by doing:

from glob import glob
try:
    result = subprocess.run(
        ["cat"] + glob("/etc/*-release"),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True)
except subprocess.TimeoutExpired as err:
    result = err
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
tito
  • 12,990
  • 1
  • 55
  • 75
  • are we sure redirecting both output & error to `PIPE` cannot deadlock? – Jean-François Fabre Mar 27 '18 at 13:15
  • BTW there's no timeout possible here. – Jean-François Fabre Mar 27 '18 at 13:15
  • 1
    The first argument can be `'cat'`, not `'/bin/cat'` -- a PATH lookup will happen as necessary. – Charles Duffy Mar 27 '18 at 13:30
  • I have not come across deadlock issues, however, this solution throws the same error. – Payam Mesgari Mar 27 '18 at 13:32
  • @PayamMesgari, what does `glob('/etc/*-release')` return if you run it on its own at the REPL (after the `import` line in this answer)? It would only be *the same* error if that glob expression returns the original string. – Charles Duffy Mar 27 '18 at 13:32
  • It returns a list with two items in it – Payam Mesgari Mar 27 '18 at 13:35
  • @PayamMesgari, ...are those items names of files that actually exist? If they exist, and you're passing them on the command line to `cat` (as this answer tells you to), then how/why would it actually throw a `No such file or directory` error? – Charles Duffy Mar 27 '18 at 13:35
  • @PayamMesgari, ...anyhow, if `'/etc/*-release'` isn't one of the results in that list, then you can't possibly be getting `cat: '/etc/*-release': No such file or directory` as the resulting error if you're using this code verbatim. – Charles Duffy Mar 27 '18 at 13:37
  • @Jean-FrançoisFabre How would we deadlock by piping both `stderr` and `stdout` to the same pipe? There might be issues having two different writers to the same pipe, but dead-lock is not one of them, as far as I can understand. – JohanL Mar 27 '18 at 13:58
  • @CharlesDuffy Now I have a doubt about this. Doesn't `run` perform a `communicate` internally? The quote I copied is from the `call` function. legacy functions don't do that but `run` does. Why wouldn't it after all? Anyone can implement it using `Popen` and `communicate`. – Jean-François Fabre Mar 27 '18 at 15:48
  • (note that my first comment was rather interrogative, not affirmative). Interesting. – Jean-François Fabre Mar 27 '18 at 15:51
  • @Jean-FrançoisFabre, ...reading the Python 3.6 source, it does indeed look like it does the Right Thing. I'm going to need to track back which version the docs quoted previously refer to. – Charles Duffy Mar 27 '18 at 15:55
  • it's the same documentation page, but it only applies to `call` and `check_call` – Jean-François Fabre Mar 27 '18 at 15:56
-3

You can use the bash command so the * can be evaluated:

process =  subprocess.run(['bash', '-i', '-c',  'cat /etc/*-release'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Yassine Faris
  • 951
  • 6
  • 26
  • 2
    This entirely defeats the purpose of the exercise. `shell=True` will prefix your argument list with `['sh', '-c']`; if the OP were fine with a shell (which they shouldn't be), they could have just used that. – Charles Duffy Mar 27 '18 at 13:31