subprocess.call 使用字符串与使用列表
- 2025-02-13 08:35:00
- admin 原创
- 43
问题描述:
我正在尝试将 rsync 与 subprocess.call 结合使用。奇怪的是,如果我向 subprocess.call 传递一个字符串,它就可以工作,但传递列表则不行。
使用字符串调用 sp.call:
In [23]: sp.call("rsync -av content/ writings_raw/", shell=True)
sending incremental file list
sent 6236 bytes received 22 bytes 12516.00 bytes/sec
total size is 324710 speedup is 51.89
Out[23]: 0
使用列表调用 sp.call:
In [24]: sp.call(["rsync", "-av", "content/", "writings_raw/"], shell=True)
rsync version 3.0.9 protocol version 30
Copyright (C) 1996-2011 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
64-bit files, 64-bit inums, 32-bit timestamps, 64-bit long ints,
socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
append, ACLs, xattrs, iconv, symtimes
rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you
are welcome to redistribute it under certain conditions. See the GNU
General Public Licence for details.
rsync is a file transfer program capable of efficient remote update
via a fast differencing algorithm.
Usage: rsync [OPTION]... SRC [SRC]... DEST
or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST
or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST
or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST
or rsync [OPTION]... [USER@]HOST:SRC [DEST]
or rsync [OPTION]... [USER@]HOST::SRC [DEST]
or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]
The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect
to an rsync daemon, and require SRC or DEST to start with a module name.
Options
-v, --verbose increase verbosity
-q, --quiet suppress non-error messages
--no-motd suppress daemon-mode MOTD (see manpage caveat)
... snipped....
repeated: --filter='- .rsync-filter'
--exclude=PATTERN exclude files matching PATTERN
--blocking-io use blocking I/O for the remote shell
-4, --ipv4 prefer IPv4
-6, --ipv6 prefer IPv6
--version print version number
(-h) --help show this help (-h is --help only if used alone)
...snipped ...
rsync error: syntax or usage error (code 1) at main.c(1438) [client=3.0.9]
Out[24]: 1
我使用列表的方式有什么问题?你会如何修复它?我需要列表,因为我想使用变量。当然我可以使用:
sp.call("rsync -av "+Orig+" "+Dest, shell=True)
但我想了解如何subprocess
理解列表和字符串。
设置 shell=False 和列表:
In [36]: sp.call(['rsync', '-av', ORIG, DEST], shell=False)
sending incremental file list
sent 6253 bytes received 23 bytes 12552.00 bytes/sec
total size is 324710 speedup is 51.74
Out[36]: 0
设置 shell=False 和一个字符串
In [38]: sp.call("rsync -av"+" "+ORIG+" "+DEST, shell=False)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-38-0d366d3ef8ce> in <module>()
----> 1 sp.call("rsync -av"+" "+ORIG+" "+DEST, shell=False)
/usr/lib/python2.7/subprocess.pyc in call(*popenargs, **kwargs)
491 retcode = call(["ls", "-l"])
492 """
--> 493 return Popen(*popenargs, **kwargs).wait()
494
495
/usr/lib/python2.7/subprocess.pyc in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
677 p2cread, p2cwrite,
678 c2pread, c2pwrite,
--> 679 errread, errwrite)
680
681 if mswindows:
/usr/lib/python2.7/subprocess.pyc in _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite)
1257 if fd is not None:
1258 os.close(fd)
-> 1259 raise child_exception
1260
1261
OSError: [Errno 2] No such file or directory
解决方案 1:
subprocess
处理命令参数的规则实际上有点复杂。
一般来说,要运行外部命令,您应该使用shell=False
并传递序列形式的参数。shell=True
仅当需要使用 shell 内置命令或特定 shell 语法时才使用;shell=True
正确使用与平台相关,如下所述。
来自文档:
args
应为程序参数序列,否则应为单个字符串。默认情况下,如果为序列,则要执行的程序是序列中的第一个项args
。args
如果args
为字符串,则解释取决于平台,如下所述。请参阅shell
和executable
参数以了解与默认行为的其他差异。除非另有说明,否则建议args
以序列形式传递。如果shell
为 True,则建议args
以字符串而不是序列形式传递。
和shell=False
:
在 Unix 上,如果
args
是字符串,则该字符串将被解释为要执行的程序的名称或路径。但是,这只有在没有向程序传递参数的情况下才能完成。在 Windows 上,如果
args
是序列,它将按照在Windows 上将参数序列转换为字符串中描述的方式转换为字符串。这是因为底层CreateProcess()
对字符串进行操作。
和shell=True
:
在带有 的 Unix 上
shell=True
,shell 默认为/bin/sh
。如果args
是字符串,则该字符串指定要通过 shell 执行的命令。这意味着字符串的格式必须与在 shell 提示符下键入时完全相同。这包括例如用引号或反斜杠转义带有空格的文件名。如果 args 是序列,则第一项指定命令字符串,任何其他项将被视为 shell 本身的附加参数。在带有 的 Windows 上
shell=True
,COMSPEC
环境变量指定默认 shell。在 Windows 上,唯一需要指定的情况shell=True
是您希望执行的命令内置于 shell 中(例如dir
或copy
)。您不需要shell=True
运行批处理文件或基于控制台的可执行文件。
(重点均由我强调)
为了完整起见,下面列出了 UNIX 系统上四个示例中发生的情况:
字符串shell=True
subprocess.call("rsync -av a/ b/", shell=True)
将调用sh -c "rsync -av a/ b/"
,执行shell脚本rsync -av a/ b/
;shell会将其解析为对带有参数的调用,,,,rsync
因此-av
它可以正常工作。a/
`b/`
请注意,如果任何参数包含空格或特殊的 shell 字符,则需要手动转义,这是一种脆弱的方法。
列出shell=True
subprocess.call(["rsync", "-av", "a/", "b/"], shell=True)
将调用sh -c "rsync" -av a/ b/
,执行 shell 脚本rsync
,将 $0 设置为-av
,将 $1 设置为a/
,将 $2 设置为b/
。此 shell 脚本调用时rsync
不带任何参数(忽略 $0、$1、$2),这就是您看到满屏帮助文本的原因。
实现此目的的一种方法是subprocess.call(['rsync "$@"', "rsync", "-av", "a/", "b/"], shell=True)
。这将调用一个 shell 脚本,该脚本将参数传递给。请注意,需要设置的rsync
虚拟额外参数(请注意 的扩展以 $1 开头)。这不是一个理想的解决方案,因此很少使用 序列。rsync
`$0$@
shell=True`
字符串shell=False
subprocess.call("rsync -av a/ b/")
将尝试查找名为rsync -av a/ b/
$PATH 的二进制文件。由于不存在这样的二进制文件,因此您会收到错误subprocess
。使用字符串时无法向程序提供任何参数shell=False
。
列出shell=False
subprocess.call(["rsync", "-av", "a/", "b/"])
调用rsync
$PATH 上的二进制文件,传递rsync
参数为 argv[0]、-av
argv[1]、a/
argv[2] 和b/
argv[3]。无需对参数进行转义,因为它们会直接传递到execve
系统调用。