Common Lisp: Launch subprocess with different working directory than lisp process

依然范特西╮ 提交于 2019-12-19 09:13:06

问题


Suppose I have a directory A, and subdirectory B. I cd into A and launch lisp. In that lisp process, I would like to launch a Python subprocess where Python sees B as its current working directory. The lisp process needs to have cwd in A, and the python process should have cwd in B. How do I do this in a cross-platform, simple way?

I'm looking for a solution that works with CCL and SBCL (probably using 'run-program function), and works for Windows, Linux, and OS X.

I looked at the CCL run-program documentation, and I didn't see a way to change the cwd of the launched process.

I looked at Python command-line arguments, and I didn't see one that would change the cwd of the python process.

I thought about a run-program call for 'cd B; python ...', but I'm not sure how that would work, since it's really running two programs; cd, and then python.

The Python code is being provided as input (as a file), so I cannot change any of that code (by adding an os.chdir() call or similar).

A python wrapper file that launches the python input file as a subprocess isn't ideal, because I'm sending stdin and listening to stdout of the python process launched by lisp. Adding another subprocess in between lisp and the python process that evals the input file means I'd need to do a lot of stout/stdin relaying, and I have a feeling that this would be brittle.

krzysz00's approach worked very well. Since the directory change is handled in lisp, before the python process is launched, this approach will work for launching other processes in different subdirectories (not just python).

For documentation, here's my code using krzsz00's approach that worked for SBCL & CCL. Note that it uses Hoyte's defmacro! macro, from Let Over Lambda, to easily avoid unwanted variable capture:

#+:SBCL
(defun cwd (dir)
  (sb-posix:chdir dir))

(defun getcwd ()
  #+SBCL (sb-unix:posix-getcwd)
  #+CCL (current-directory))

(defmacro! with-cwd (dir &body body)
  `(let ((,g!cwd (getcwd)))
     (unwind-protect (progn
                       (cwd ,dir)
                       ,@body)
     (cwd ,g!cwd))))

Usage:

(with-cwd "./B"
  (run-program ...))

回答1:


To run external programs (like your python process portably) see external-program. To change the current working directory, use this slightly modified (public domain) function cwd from the file http://files.b9.com/lboot/utils.lisp, which is reproduced below.

(defun cwd (&optional dir)
  "Change directory and set default pathname"
  (cond
   ((not (null dir))
    (when (and (typep dir 'logical-pathname)
           (translate-logical-pathname dir))
      (setq dir (translate-logical-pathname dir)))
    (when (stringp dir)
      (setq dir (parse-namestring dir)))
    #+allegro (excl:chdir dir)
    #+clisp (#+lisp=cl ext:cd #-lisp=cl lisp:cd dir)
    #+(or cmu scl) (setf (ext:default-directory) dir)
    #+cormanlisp (ccl:set-current-directory dir)
    #+(and mcl (not openmcl)) (ccl:set-mac-default-directory dir)
    #+openmcl (ccl:cwd dir)
    #+gcl (si:chdir dir)
    #+lispworks (hcl:change-directory dir)
    #+sbcl (sb-posix:chdir dir)
    (setq cl:*default-pathname-defaults* dir))
   (t
    (let ((dir
       #+allegro (excl:current-directory)
       #+clisp (#+lisp=cl ext:default-directory #-lisp=cl lisp:default-directory)
       #+(or cmu scl) (ext:default-directory)
       #+sbcl (sb-unix:posix-getcwd/)
       #+cormanlisp (ccl:get-current-directory)
       #+lispworks (hcl:get-working-directory)
       #+mcl (ccl:mac-default-directory)
       #-(or allegro clisp cmu scl cormanlisp mcl sbcl lispworks) (truename ".")))
      (when (stringp dir)
    (setq dir (parse-namestring dir)))
      dir))))

Combining these two functions, the code you want is:

(cwd #p"../b/")
(external-program:start "python" '("file.py") :output *pythins-stdout-stream* :input *pythons-stdin-stream*)
(cwd #p"../a/")

This will cd to B, run the python process as if by python file.py &, send the python process's stdin/stdout to the specified streams (look at the external-program documentation for more details), and finally execute another cwd that returns the lisp process to A. If the lisp process should wait until the python process is finished, use external-program:run instead of external-program:start.




回答2:


I ended up writing krzysz00's suggestion up into a package that can be found here.

Then someone pointed out that UIOP comes with getcwd and chdir. If you have a fairly recent lisp, UIOP should come included with your edition of asdf.




回答3:


I dont know what lisp is but could this work?

  import subprocess
  subprocess.Popen('python myscript.py', cwd='B')

http://docs.python.org/library/subprocess.html



来源:https://stackoverflow.com/questions/10049338/common-lisp-launch-subprocess-with-different-working-directory-than-lisp-proces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!