check_call() 子进程中出错,执行 'mv' unix 命令:"Syntax error: '(' unexpected"



我正在为Travis CI.制作一个python脚本

.travis.yml

...
script:
  - support/travis-build.py
...

python文件travis-build.py是这样的:

#!/usr/bin/env python
from subprocess import check_call
...
check_call(r"mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder", shell=True)
...

当Travis大楼达到这条线时,我得到了一个错误:

/bin/sh: 1: Syntax error: "(" unexpected

我只是尝试了很多不同的形式来写它,但我得到了相同的结果。知道吗?

提前感谢!

编辑

我当前的目录布局:

- my_project/final_folder/
- cmake-3.0.2-Darwin64-universal/
- fileA
- fileB
- fileC

我正在尝试使用此命令将所有当前文件fileAfileBfileC(不包括my_projectcmake-3.0.2-Darwin64-universal文件夹)移动到./my_project/final_folder中。如果我在Linux shell上执行这个命令,我会得到目标,但不是通过check_call()命令。

注意:我无法逐个移动文件,因为还有很多其他

我不知道Travis默认使用的是哪个shell,因为我没有指定它,我只知道如果我在.Travis.yml:中编写命令

.travis.yml

...
script:
  # Here is the previous Travis code
  - mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder
...

它有效。但如果我使用脚本,它就会失败。

我从以下问题中找到了此命令:

如何使用"mv"命令移动特定目录中的文件以外的文件?

您正在使用bash功能extglob,试图排除您指定的文件。您需要启用它才能使它排除您指定的两个条目。

当您使用shell=True时,python子流程模块显式地使用/bin/sh默认情况下不会启用类似这样的bash功能(使其更像原始sh是一件符合性的事情)。

如果您想让bash来解释该命令;您必须显式地将其传递给bash,例如使用:

subprocess.check_call(["bash", "-O", "extglob", "-c", "mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder"])

不过,我不会选择以这种方式来做这项工作。

让我再试一次:您希望语法!(...)在哪个shell中工作?是狂欢吗?是ksh吗?我从未使用过它,快速搜索相应的bash功能也无济于事。我怀疑您的语法是错误的,这就是错误消息告诉您的。在这种情况下,您的问题完全独立于python和subprocess模块。

如果系统上有一个特殊的shell支持这种语法,则需要确保Python在调用命令时使用相同的shell。它告诉您它一直在使用哪个shell:/bin/sh。这通常只是指向真正的shell可执行文件的链接。它是否指向您在其中测试命令的同一个shell?

编辑:您引用的SO解决方案包含注释中的解决方案:

提示:但是请注意,使用此模式依赖于extglob。你可以使用shopt的extglob启用它(如果您希望扩展glob默认情况下打开后,您可以将shopt的extglob添加到.bashrc)

为了证明不同的shell可能以不同的方式处理您的语法,首先使用bash:

$ !(uname)
-bash: !: event not found

然后,使用/bin/dash:

$ !(uname)
Linux

subprocess.something方法的参数必须是命令行参数列表。使用例如shlex.split()将字符串拆分为正确的命令行参数:

import shlex, subprocess
subprocess.check_call( shlex.split("mv !(...)") )

编辑:因此,目标是移动文件/目录,但某些文件/目录除外。通过玩bash,我可以让它像这样工作:

mv `ls | grep -v -e '(exclusion1|exclusion2)'` my_project

所以在你的情况下,这将是:

mv `ls | grep -v -e '(myproject|cmake-3.0.2-Darwin64-universal)'` my_project

这可能会进入subprocess.check_call(..., shell=True),它应该做你期望它做的事情。

最新更新