首页 > 解决方案 > 从 bash 脚本中运行时,flake8 的行为不同

问题描述

我想这里的答案可能是微不足道的,但我可能需要一些复杂的 bash 知识。我已经浏览 bash 文档几个小时了,但似乎找不到答案。

我正在开发一个 python 存储库,并提出了一个简单的脚本来仅对当前分支和主分支之间不同的文件进行 lint。这是从所述脚本 ( lint.sh) 中提取的最小工作示例:

#!/bin/bash    
paths=$(git diff --name-only -r origin/master...HEAD | grep \.py$)    
flake8 $paths

出于测试目的,假设我只提交了一个文件,bad.py,其内容如下:

hello
there

的预期输出bash lint.sh为:

bad.py:1:1: F821 undefined name 'hello'
bad.py:2:1: F821 undefined name 'there'

但是,输出为空。在调试模式下运行时,bash 显示以下命令:

++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths='bad.py'
+ flake8 'bad.py'

这是我所期望的。另外,当我简单地运行时flake8 bad.py,输出符合预期。

我希望这可能与不同 bash 版本之间不同的参数传递有关。的输出bash --versionGNU bash, version 4.4.23(1)-release (x86_64-apple-darwin17.5.0)

我会欣赏所有的见解

标签: bashgitflake8

解决方案


非常抱歉,这不完全是一个答案,但它肯定不适合评论!

这里给我的提示如下:

+ paths='bad.py'
+ flake8 'bad.py'

在我执行相同的脚本时,我得到以下信息:

$ bash -x lint.sh 
++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths=bar.py
+ flake8 bar.py
bar.py:1:1: F821 undefined name 'hello'
bar.py:2:1: F821 undefined name 'world'

请注意,我的输出如何包含文件名或分配的引号。 bash除非必要,否则通常不会添加引号。这告诉我的是,该字符串中可能有某种控制字符(我最好的猜测是颜色或\b+ 一些其他字符(这可能是截图实际上有用的少数情况之一!))。

这是我能够重现您的发现的一种方法:

mkdir -p bin

cat > bin/grep << EOF
#!/usr/bin/env bash
exec /bin/grep --color=always "\$@"
EOF

chmod +x bin/grep

# simulate having this `grep` on your path
PATH=$PWD/bin:$PATH bash -x lint.sh

(虽然这似乎是一件奇怪的事情,但在过去我已经把我自己的东西放进去grep~/bin所以我--line-buffered --color=auto现在可以添加已GREP_OPTIONS弃用的东西——人们可能会错误地添加--color=always并让它工作......在大多数情况下)。今天我改用别名,因为即使这样我也遇到了尖锐的边缘。

这种情况下的输出与上面的输出相匹配:

$ PATH=$PWD/bin:$PATH bash -x lint.sh
++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths='bar.py'
+ flake8 'bar.py'

但棘手的提示是突出显示

终端输出截图

附录

虽然与您的问题无关,但这里可能是完成您想要的更好的方法:

# if you have GNU xargs
git diff -z --name-only origin/master...HEAD -- '*.py' | xargs --null --no-run-if-empty flake8

# if you need to be more portable (I see you're probably on macos)
git diff -z --name-only origin/master...HEAD -- '*.py' | xargs -0 flake8 /dev/null

不同部分的解释:

  • git diff -z: 输出带有空字节分隔的文件名。如果文件名包含空格或其他特殊字符,这可以防止拼接
  • xargs --null:在喷射参数时将输入拆分为空字节
  • xargs --no-run-if-empty:如果没有参数,就不要运行可执行文件(这是一个 GNU 扩展)
  • xargs -0: 与 : 相同xargs --null,但是如果你坚持使用非 GNU xargs,你将无法访问长选项
  • flake8 /dev/null:这是一个偷偷摸摸的把戏,因为 bsd xargs 没有“如果为空则不运行”选项,它总是会调用flake8. 如果flake8使用零参数调用,则默认为递归当前工作目录(并检查所有文件)。通过放在/dev/null开头,这可以防止这种行为,而是 lints 一个空文件!

附录 2,您可能想要考虑使用 git hooks 框架来为您处理所有这些,我维护pre-commit旨在消除周围的许多粗糙边缘git(例如这个!)。


推荐阅读