0

I can check the presence of a file or folder using OS library very easily. The following two links have described that directoryExistance fileExistance

I am attempting to use the subprocess library to do the same

and, I tried a couple of approaches already

1- status = subprocess.call(['test','-e',<path>]), which is always returning 1, no matter what I pass in path.

2- Using getstatusoutput,

/bin/sh: 1: : Permission denied

status, result = subprocess.getstatusoutput([<path>])
print(status)
print(result)

which is working fine because status variable returns 126 if the file/folder exist and 127 when the file/folder doesn't exist. Also the result variable contains message but the "result" variable contains the message : Permission denied

But the second solution looks like a hack to me. Is their a better way, of doing this ?

Manish
  • 668
  • 1
  • 6
  • 13
  • "/bin/sh: 1: : Permission denied", is written by mistake in the second point. – Manish Nov 21 '17 at 18:37
  • 1
    The modern way is to use [`pathlib`](https://docs.python.org/3/library/pathlib.html#basic-use). – PM 2Ring Nov 21 '17 at 18:45
  • Not working with '~'. In my ubuntu platform – Manish Nov 21 '17 at 19:05
  • I need my path to be somethig like '~/workspace/datasets'. – Manish Nov 21 '17 at 19:06
  • You can use `Path.home()` to make a '~' path. – PM 2Ring Nov 21 '17 at 19:12
  • @ManishSharma. For me, your first example works fine. Are you aware that `test` returns **zero** on success and **non-zero** on failure? If you want tilde expansion, you must use e.g. `os.path.expanduser('~/workspace')`. – ekhumoro Nov 21 '17 at 19:13
  • `~` is a shell expansion. If you want that to work in a `call()`, you'll need to add `shell=True`. – glibdud Nov 21 '17 at 19:14
  • 2
    @glibdud. Using `shell=True` only works if the command is a string, rather than a list, and there are [security considerations](https://docs.python.org/3/library/subprocess.html#security-considerations) to take into account. – ekhumoro Nov 21 '17 at 19:22
  • @glibdud, it worked, it was a stupid mistake on my part, shell=True worked. Thanks – Manish Nov 21 '17 at 19:22
  • @glibdud, it worked for me, thanks for bringing in the security consideration. I am reading the username myself and generating the path separately to keep the shell=False. Is it the right way to do it ? – Manish Nov 21 '17 at 19:26
  • @ekhumoro There are certainly security considerations, but `shell=True` works fine with a list. – glibdud Nov 21 '17 at 19:28
  • @glibdud Also, I noticed that the first way is not allowing me to distinguish between a file and a folder – Manish Nov 21 '17 at 19:29
  • @glibdud. No, it does not (on linux, anyway). – ekhumoro Nov 21 '17 at 19:30
  • @glibdud, in that case I will be probably going with pathlib library. – Manish Nov 21 '17 at 19:31
  • @ManishSharma At any rate, I'm not sure why you're deciding to do it this way rather than just using `os`. – glibdud Nov 21 '17 at 19:33
  • @glibdud. For me, `call(['test', '-e', '~'], shell=True)` returns `1`, but `call('test -e ~', shell=True)` returns `0`. – ekhumoro Nov 21 '17 at 19:34
  • From Python docs: The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions: os.system os.spawn* – Manish Nov 21 '17 at 19:35
  • @ManishSharma That just means they're trying to replace those specific functions, not the whole module. – glibdud Nov 21 '17 at 19:36
  • @glibdud, Yeah, adding shell=True works – Manish Nov 21 '17 at 19:36
  • @ManishSharma. Why are you bothering to use the shell for this? It seems totally pointless and inefficient. – ekhumoro Nov 21 '17 at 19:38
  • Make a lot more sense to me. I should have read it carefully. Thanks for the clarification. – Manish Nov 21 '17 at 19:39

2 Answers2

1

The test command is a shell builtin, and on many platforms doesn't exist as an independent command you can run.

If you use shell=True to use the shell to run this command, you should pass in a single string, not a list of tokens.

status = subprocess.call("test -e '{}'".format(path), shell=True)

This will produce a malformed command if path contains any single quotes; try path.replace("'", r"\'") if you want to be completely correct and robust, or use one of the existing quoting functions to properly escape any shell metacharacters in the command you pass in.

The subprocess library now offers a function run() which is slightly less unwieldy than the old legacy call() function; if backwards compatibility is not important, you should probably switch to that... or, as several commenters have already implored you, not use subprocess for this task when portable, lightweight native Python solutions are available.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • For what it's worth, `test -e` merely checks whether an entry with the given name exists in the file system. Perhaps you are actually looking for `test -f` and `test -d` which additionally check whether it refers to a regular file or a directory, respectively. – tripleee Nov 21 '17 at 21:10
  • Actually the `replace` hint is wrong, but too lazy to go back and fix it. Probably just use `shlex.quote(path)` and take out the single quotes. – tripleee Mar 26 '21 at 08:32
0

As pointed in the comments section

status = subprocess.call(['test','-e',<path>]) 

can be made to work with a shell expansion if we use "shell=True"

Although using os.path might be much more efficient anyways.

Manish
  • 668
  • 1
  • 6
  • 13