当 运行 来自 bash 脚本时,flake8 的行为不同

flake8 behaves differently when run from within a bash script

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

我正在处理一个 python 存储库,并提出了一个简单的脚本来仅检查当前分支和主分支之间不同的文件。这是从所述脚本 (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 --version 的输出: GNU bash, version 4.4.23(1)-release (x86_64-apple-darwin17.5.0)

我会感谢所有见解

非常抱歉,这不是一个准确的答案,但肯定不适合发表评论!

给我的提示如下:

+ 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 is deprecated -- one might erroneously add --color=always and have it work... for the most part). Today I use an alias,因为我 运行 即使那样也会进入锐边。

这种情况下的输出与您上面的相符:

$ 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 没有 "no run if empty" 选项,它总是会调用 flake8 .如果 flake8 被零参数调用,它默认递归你当前的工作目录(并检查你所有的文件)。通过将 /dev/null 放在开头,这可以防止这种行为,而是检查一个空文件!

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