1

I'm using check_output to do all my SSH and GitHub setup, and I'm attempting to execute eval $(ssh-agent), both to start the agent and to parse the output if I need the process id.

from subprocess import check_output

out = check_output(["eval", "$(ssh-agent)"])
print(out)

But regardless of how I escape things, I get the same error.

Traceback (most recent call last):
  File "gitSetup.py", line 3, in <module>
    out = check_output(["eval", "$(ssh-agent)"])
  File "/usr/lib/python2.7/subprocess.py", line 216, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

I'm wondering if I'm not escaping or trimming my arguments properly; if anyone sees my error, I'd appreciate some insight!

Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
rainydaymatt
  • 594
  • 3
  • 10
  • 21
  • 1
    Depends on your OS and shell, but I'm guessing `eval` is a shell function rather than an actual program that exists in the filesystem. If so, you'll need to use `shell=True` to have access to it. – glibdud Dec 27 '18 at 20:07
  • related: https://discuss.ocaml.org/t/is-eval-opam-env-switch-switch-set-switch-equivalent-to-opam-switch-set-switch/10957 – Charlie Parker Dec 14 '22 at 01:55

1 Answers1

1

Even if you managed to fix the superficial syntax problems, a subprocess which runs successfully simply then terminates without a trace; it doesn't attempt to (and couldn't, even if it wanted to) modify the environment of the parent Python process. This is a common FAQ.

You could run the eval in the parent process which starts your Python script, or perhaps figure out how to communicate with ssh-agent directly from Python. Its output is usually a simple sequence of variable assignments, which you can parse yourself.

from subprocess import check_output
from os import environ

eval_string = check_output(['ssh-agent'])
for line in eval_string.rstrip('\n').split('\n'):
    for expr in line.rstrip(';').split(';'):
        if expr.startswith((' export ', 'echo ')):
            continue
        var, value = expr.strip().split('=', 1)
        environ[var] = value

If the output from ssh-agent contains shell quoting, you will need to perform additional parsing on it (basically, trim the quotes around the value string). But this is already rather clunky and brittle, so perhaps revert to setting up the environment before launching Python instead of trying to splash some sophistication onto this kludge.

In more detail, ssh-agent and a precious few other shell utilities have a very specific design in order for them to be able to communicate with their parent process. Precisely because a subprocess cannot make any changes in the environment of its parent process, it instead prints a piece of code for its parent process to execute. By default, it prints sh code like this:

SSH_AUTH_SOCK=/tmp/ssh-MUyniqn10506/agent.10506; export SSH_AUTH_SOCK;
SSH_AGENT_PID=10507; export SSH_AGENT_PID;
echo Agent pid 10507;

There is also an option for it to print similar code in csh syntax (this is a historical shell which thankfully isn't used much any longer) but, alas, no option for producing Python code. (It would not be hard to make ssh-agent do that, per se.)

(The above output sample copy/pasted from http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/ which contains a fuller explanation.)

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • aren't you missing `.decode()`? – Charlie Parker Dec 15 '22 at 07:21
  • 1
    @CharlieParker I would generally prefer to add `text=True` if necessary. This was posted back when Python 2 was still supported, and that behaved differently; as you can see from the traceback, the OP was on 2.7. But yeah, for Python 3, you get raw bytes by default. – tripleee Dec 17 '22 at 11:15