【问题标题】:Error in check_call() subprocess, executing 'mv' unix command: "Syntax error: '(' unexpected"check_call() 子进程出错,执行 'mv' unix 命令:“语法错误:'(' 意外”
【发布时间】:2015-04-11 19:48:00
【问题描述】:

我正在为 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

...

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

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

How to use 'mv' command to move files except those in a specific directory?

【问题讨论】:

  • 您是否尝试进行进程替换?如果是这样,语法将是$(my_project|cmake-3.0.2-Darwin64-universal)
  • 您希望您的语法在哪些类型的 shell 中工作?
  • @Tom 我没试过,我改一下再证明
  • @Jan-PhilipGehrcke 我希望它能在 sh shell 上工作
  • 你需要明确你想要做什么,因为目前,我们只是在猜测。 my_project 是命令吗?您是否希望将其通过管道传输到命令cmake-3.0.2-Darwin64-universal 的输出用作mv 的第一个参数?如果没有,尝试我的建议毫无意义!

标签: python bash shell sh travis-ci


【解决方案1】:

您正在使用 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"])

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

【讨论】:

  • 我完全不知道这个 bash 扩展。你每天都会学到新东西......
  • 我觉得这是一种复杂的通配符语法,但谷歌搜索相应的 bash 文档并非易事。现在我发现这个文档看起来很干净而且很有帮助:wiki.bash-hackers.org/syntax/pattern
  • 我也无法通过谷歌轻松找到它。但是 GNU bash 文档确实提到了它(如果您知道要查找的内容:gnu.org/software/bash/manual/html_node/…
【解决方案2】:

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

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

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

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

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

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

然后,使用 /bin/dash:

$ !(uname)
Linux

【讨论】:

    【解决方案3】:

    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),它应该做你期望它做的事情。

    【讨论】:

    • 谢谢@haavee,我刚试过但现在收到:mv: cannot stat '!(my_project|cmake-3.0.2-Darwin64-universal)': No such file or directory
    • 我会说“!”里面有错误。可能它应该是 $,因为看起来您需要替换 '()' 之间的任何内容的输出。此外,“|”里面的管道很可疑。 “mv”命令究竟应该做什么?
    • shell=True 被传入,所以不,OP 应该将命令拆分为列表。他们明确使用外壳功能。 !(...) 在执行移动之前首先由 shell 解释。
    • 好的,然后去掉"shell=True" ;-)
    • 不,因为那样你就不能在你的命令中使用 shell 特性了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    • 2022-01-18
    • 2017-11-07
    • 1970-01-01
    • 2016-06-22
    • 1970-01-01
    • 2015-02-06
    相关资源
    最近更新 更多