【问题标题】:subprocess cp leaves some files empty子进程 cp 将一些文件留空
【发布时间】:2017-12-08 21:55:40
【问题描述】:

我正在尝试将一些文件从一个目录复制到另一个目录。我希望一个目录中的所有文件都位于另一个目录的根目录中。

当我在终端中运行它时,这个命令完全符合我的要求:

cp -rv ./src/CopyPasteIntoBuildDir/* ./build-root/src/

然而,这行 python 复制了大部分文件,就像上面的命令一样,但是它使一些新文件为空。具体来说,子目录中的文件是空的。

subprocess.check_call("cp -rv ./src/CopyPasteIntoBuildDir/* ./build-root/src/", shell=True)

如果文件不存在则创建文件,如果存在则截断它们。

发生了什么事?

【问题讨论】:

  • 我敢打赌 shell 参数扩展是罪魁祸首,你试过:subprocess.check_call(["cp", "-rv", "./src/CopyPasteIntoBuildDir/*", "./build-root/src/"]) 吗?另外,您知道shutil 模块,对吗?
  • @zwer 那行不通。很确定这是因为*。 Shutil 似乎没有“复制此文件夹中的所有内容并将其转储到另一个文件夹中”的功能:( 必须os.walk 我通过,我只是没有重新实现cp 那样。跨度>
  • os.system("cp -rv ./src/CopyPasteIntoBuildDir/* ./build-root/src/") 结果相同;将子目录中的文件留空。
  • Python用的是sh,我用的是bash;也许这就是罪魁祸首?
  • shutil.copytree() 复制整个树,不需要os.walk()

标签: python macos subprocess


【解决方案1】:

假设您决定使用 cp 而不是原生 Python 操作 --

如果你把这段代码写成不调用任何shell,它会更加可靠。为了避免在源上需要/*(以及这样做的副作用——即拒绝复制名称超过ARG_MAX组合环境和命令行大小存储限制的目录),请使用.作为要复制其内容的目录名称的最后一个元素,而不是传递需要由 shell 扩展的通配符。

subprocess.check_call(["cp", "-R", "--", '%s/.' % src, dest])

使用cp -R 而不是cp -rv 是因为-R 而不是-r,是POSIX 标准化的(因此可在所有兼容的类UNIX 平台上移植)。


实际演示(复制/粘贴代码)

tempdir=$(mktemp -d -t testdir.XXXXXX)
trap 'rm -rf "$tempdir"' EXIT

cd "$tempdir"
mkdir -p ./src/CopyPasteIntoBuildDir/subdir-1 ./build-root/src/
touch ./src/CopyPasteIntoBuildDir/file-1
touch ./src/CopyPasteIntoBuildDir/subdir-1/file-2

script='
import sys, shutil, subprocess

src = sys.argv[1]
dest = sys.argv[2]
subprocess.check_call(["cp", "-R", "--", "%s/." % src, dest])
'

python -c "$script" ./src/CopyPasteIntoBuildDir ./build-root/src/
find ./build-root -type f -print
rm -rf "$tempdir"

...发出类似于以下内容的输出:

./build-root/src/file-1
./build-root/src/subdir-1/file-2

...显示内容被正确递归复制,没有前缀。

【讨论】:

    【解决方案2】:

    显然这是sh 的问题。改用 bash 就可以了。

    subprocess.check_call("cp -rv ./src/CopyPasteIntoBuildDir/* ./build-root/src/", shell=True, executable="/bin/bash")
    

    编辑:查看接受的答案!

    【讨论】:

    • 这很难相信。 cp 不是 either shbash 的一部分——无论您的操作系统供应商是否提供 /bin/usr/bin哪个 shell 调用它。 (唯一的例外是/bin/sh 是busybox 的地方,但在这种环境中/bin/cp 通常 是busybox。
    • @CharlesDuffy - 在他的系统上,sh 的别名设置为cp 的可能性很小...
    • @zwer, ...仅当有一个名为 ENV 的环境变量指向建立此类别名的脚本时(并且 /bin/sh 的副本实际上支持非交互式 shell 中的别名,即异常)。否则,不会在以sh -c '...' 开头的非交互式shell 中读取点文件(因为subprocess.check_call() 使用shell=True 调用,代码在... 参数中传递),因此无法定义别名。
    • @zwer, ...granted, /bin/bash 将尊重 BASH_ENV 而不是 ENV 作为在初始化时读取的脚本,并且 那些 可能正在更新PATH 包含具有不同版本的cp 的位置,但同样,这都是非常不寻常的——我可以数出我看到ENVBASH_ENV 故意使用的次数(而不是,例如,使用ENV 区分开发环境和生产环境,一方面不知道该变量具有修改非交互式shell 行为的含义)。
    • @zwer, ...bash 也将支持导出的函数,sh 不会,除非它是由 bash 提供的——但如果问题是导出函数遮蔽了 cp命令,那么我们期望/bin/sh 的行为符合标准的cpbash 的行为不符合标准。也就是说,归根结底,想象一种行为与此处描述的不同的情况需要在一些不太可能的肢体上走得很远,并且不太可能对遇到同样问题的其他人有所帮助。
    猜你喜欢
    • 2011-04-10
    • 1970-01-01
    • 2020-11-07
    • 2013-07-26
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 2013-09-30
    • 1970-01-01
    相关资源
    最近更新 更多