Search code examples
pythonbashpycharmescaping

How to replicate bash's escaping on commands passed as arguments to other commands


Edit 2: Bit of a late edit to summarise the comments on this question. I misunderstood how bash works, and confused it with how python works. I presumed Bash escaped the output of the kubectl command, so it wouldn't be broken up into multiple arguments when passed to the python command. (i.e. how you would escape quotes in python strings to stop the string being broken up.)

Actually, when surrounding the output of a command in quotes, Bash is able to pass the output of the execution of a command, as a single argument. It does not escape anything, it simply doesn't try and interpret it all either. The issue I was having is actually PyCharm's lack of functionality to pass complex strings as a single argument, and it is PyCharm that parses strings with spaces as multiple arguments.


I'm passing the result of the execution of a command to python as input, like so:

$ python parse_ips.py "$(kubectl get configmap ...)"

This works fine when executing from the command line, however I'm now trying to edit the file using PyCharm. Therefore I need the escaped version of the result of this command which I can paste into PyCharm's debug configuration, as I can't execute the command in real-time like I can do on the command line.

However, I am struggling to find a way to replicate the escaping bash does behind the scenes, so I can use the result as an argument within the PyCharm configuration. Running the above kubectl command results in a multi-line string which includes spaces and quotes. When I paste this into PyCharm it just interprets it as multiple arguments. I'm looking for the escaped result, which I could paste directly into the command line, or into PyCharm's debug configuration, to achieve the same result with a fixed parameter for testing.

Any help would be greatly appreciated!

Edit: To clarify, I mean on the command line the result of the $(kubectl ...) command is passed into the python program as a single command line argument when it is surrounded by quotes ("$(kubectl ...)"). So in the python program, you can access sys.argv[1] and it will contain the entire execution output of $(kubectl get configmap ...). However, if I execute that command myself on the command line, the result is a multi-line string.

If I then copy the result of that into PyCharm (or even on the command line again), it is interpreted as many command line arguments. E.g. it would look something like this:

$ python parse_ips.py apiVersion: v1
data:
  item1: ifconfig-push 127.0.0.0 255.255.0.0
  item2: ifconfig-push 127.0.0.1 255.255.0.0
  item3: ifconfig-push 127.0.0.2 255.255.0.0
  ...

And so on. This obviously doesn't work in the same way as it did before. So I am unable to test my program without making the kubectl call from the command line each time. I was looking to replicate what "$(kubectl ...)" gets converted into so it is able to pass the entire output as a single command line entry.


Solution

  • I am struggling to find a way to replicate the escaping bash does behind the scenes

    Typically use printf "%q" to escape stuff.

    printf "%q" "$(kubectl get configmap ....)"
    

    This is printf as the bash builtin command. It differs from coreutils printf, and newest ones also support %q with different quoting style:

    /usr/bin/printf "%q" "$(kubectl get configmap ....)"
    

    Modern bash also has quoting expansion:

    var="$(kubectl get configmap ....)"
    echo "${var@Q}"
    

    And there is also the quoting style outputted by set -x.


    I would suggest to use a file:

    kubectl get configmap ... > /tmp/tempfile
    python parse_ips.py "$(cat /tmp/tempfile)"
    

    With xclip you can copy command output straight to the X server clipboard, which is handy:

    printf "%q" "$(kubectl get configmap ...)" | xclip -selection clipboard
    
    # then in another window:
    python parse_ips.py <right mouse click><select paste>