0

I'm trying to run a find command in a directory on a remote system. Fabric changes directory sometimes but sometimes it fails, depending on whether the path contains parentheses or spaces and whether I use shlex.quote() or not. What is the correct way to handle this?

My code is basically this:

from shlex import quote
from fabric import Connection

with Connection(remote_login) as c:
    with c.cd(quote(node.src)):    # Condition 1
    # with c.cd(node.src):         # Condition 2
        result = c.run(r"nice find -maxdepth 1 -type f -printf '%f\n'", echo=True)

If I use Condition 1, it succeeds when the path contains parens. Fabric generates this line in that case:

# Fabric output success with parens in path
cd '/data/PixelSizeTestRuby105mm(Zoom248.5mm)' && nice find -maxdepth 1 -type f -printf '%f\n'

but it fails when the path contains spaces because the spaces are escaped but the path is also quoted, rather than just one or the other.

# Fabric output failure for spaces in path
cd '/data/Crystal\ Bending\ Test/Bending0' && nice find -maxdepth 1 -type f -printf '%f\n'
sh: line 0: cd: /data/Crystal\ Bending\ Test/Bending0: No such file or directory

If I instead use Condition 2, it fails for the first path and succeeds for the second.

# Fabric output failure for parens in path
cd /data/PixelSizeTestRuby105mm(Zoom248.5mm) && nice find -maxdepth 1 -type f -printf '%f\n'
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `cd /data/PixelSizeTestRuby105mm(Zoom248.5mm) && nice find -maxdepth 1 -type f -printf '%f\n''
gazzar
  • 91
  • 1
  • 5
  • You are quoting too much, either add single quotes or backslashes but not both. I'm vaguely guessing this is a bug in Fabric, but I'm not familiar with it. – tripleee Aug 03 '20 at 07:27
  • You don't have to `cd` anyway; just pass the path to `find` as the first argument, before any predicates. – tripleee Aug 03 '20 at 07:28
  • @tripleee Just to clarify, those examples with the cd commands are the output of fabric; it is forming those commands which are causing the failures. I've edited the question to try to make this clearer. I'm wondering whether it's a bug too. I want to get the cd context handling to work because I need to run other commands like md5sum which I can't find a way to run without first cd'ing to the directory. – gazzar Aug 03 '20 at 07:43
  • you shouldn’t need to quote **anything** when passing the path to `cd`. All the quoting should happen purely internally within the connection’s session implementation. If it fails for regular (i.e. unquoted) path strings, there’s a bug in that implementation. It seems that Fabric/Invoke doesn’t correctly quote parentheses. – Konrad Rudolph Aug 03 '20 at 08:43

1 Answers1

2

This is a bug in the Invoke implementation. It simply does not perform correct shell argument escape for the paths in cd.

As a quick fix, you could manually escape the parentheses in your path by adding a backslash in front. Using shlex.quote won’t work, as you’ve noticed yourself. Ideally the Invoke implementation should be fixed to use shlex.quote internally, rather than the ad-hoc, buggy manual escape it currently performs.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • thanks for your insights and suggestion to handle it manually. I'll check the Invoke issue tracker. – gazzar Aug 03 '20 at 09:04
  • @gazzar I’ve now submitted a [pull request](https://github.com/pyinvoke/invoke/pull/749) with a fix. But the project has a rather long backlog so it might not be merged very soon. – Konrad Rudolph Aug 03 '20 at 10:58
  • Great! Thanks for also saving me the effort of raising the issue. I've followed your suggestion and removed the quote() and replaced it with some targeted parentheses-escaping and my code seems to be behaving properly now. – gazzar Aug 03 '20 at 12:03