在Python脚本中运行git命令会导致语法错误

v9tzhpje  于 2023-04-10  发布在  Git
关注(0)|答案(3)|浏览(124)

当我跑的时候

git log  --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'

在Linux终端中,输出正确:

added lines: 23322, removed lines: 8536, total lines: 14786

因为我不想记住这么复杂的命令,所以我写了一个Python脚本来做同样的事情:

import os
GitCommand = 'git log  --pretty=tformat: --numstat | awk "{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }"'
report = os.system(GitCommand)

但是当我运行它时,Git报告语法错误:

awk: cmd. line:1: { add += $1; subs += $2; loc += $1 - $2 } END         { printf "added lines: %s, removed lines: %s, total lines: %s
awk: cmd. line:1:                                                                ^ unterminated string
awk: cmd. line:1: { add += $1; subs += $2; loc += $1 - $2 } END         { printf "added lines: %s, removed lines: %s, total lines: %s
awk: cmd. line:1:                                                                ^ syntax error

我也试过使用subprocess,输出也差不多,问题可能出在命令字符串的编码上,特别是引号,但我不知道如何修复。

mec1mxoz

mec1mxoz1#

Python脚本

  • (本节直接回答问题,意思是得到一个Python脚本,完成提问者想要完成的事情,然而,可能是shell脚本更合适的情况;请参阅“Shell脚本”一节了解更多信息。)*

我已经做了一个Python脚本,可以完成你想要的。我通过你的脚本制作了这个脚本,并做了两个修改:

  • 我将awk唯一参数周围的双引号改为转义单引号\'
  • 我将文本换行符\n更改为“\n”的转义版本,即\\n

下面是显示修改后的脚本工作的输出示例:

$ git log  --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'
added lines: 5, removed lines: 1, total lines: 4

$ cat script.py
import os
GitCommand = 'git log  --pretty=tformat: --numstat | awk \'{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\\n", add, subs, loc }\''
report = os.system(GitCommand)

$ python3 script.py
added lines: 5, removed lines: 1, total lines: 4

下面显示了Python脚本的差异,从问题的版本到这个答案的工作版本:

$ git diff head~ head --word-diff-regex=. script.py
diff --git a/script.py b/script.py
[...]
--- a/script.py
+++ b/script.py
@@ -1,3 +1,3 @@
import os
GitCommand = 'git log  --pretty=tformat: --numstat | awk [-"-]{+\'+}{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: % , total lines: %s\{+\+}n", add, subs, loc }[-"-]{+\'+}'
report = os.system(GitCommand)

Shell脚本

正如在这个问题的其他地方提到的,拥有一个shell脚本文件可能是简单地重复调用任何给定的shell命令的最合适的方法,无论出于什么原因,这些命令不希望存储在~/.bashrc~/.bash_profile或类似的东西中。
具体针对这个问题,举个例子:

$ git log  --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'
added lines: 5, removed lines: 1, total lines: 4

$ cat ./total-lines.sh
git log --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'

$ ./total-lines.sh
added lines: 5, removed lines: 1, total lines: 4

我的环境

$ systeminfo | grep --extended-regexp --regexp="^OS (Name|Version)"
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.19043 N/A Build 19043

$ bash --version | head --lines=1
GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)

$ git --version
git version 2.33.0.windows.2

$ python3 --version
Python 3.9.7

$ awk --version | head --lines=1
GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.1.0, GNU MP 6.2.1)
ukxgm1gy

ukxgm1gy2#

在这里使用Python增加了不必要的复杂性。这里最简单的解决方案是创建一个文件my_fancy_git_command.sh并将bash代码复制到其中。现在您可以通过使用脚本的名称来运行整个命令。
如果你想从多个目录运行这个脚本,我建议在你的用户文件夹中创建一个bin目录。然后将$HOME/bin添加到.bashrc中的PATH。一定要关闭你的终端并打开一个新的终端,以查看当前环境中对PATH的更改。

vyswwuz2

vyswwuz23#

用Python subprocess做同样的事情,它的目的是replaceos.system
根据Python文档使用shell=True的第一种方法可能存在shell注入漏洞。

import subprocess

results = subprocess.check_output("git log --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2} END { print \"added lines: \"add\", removed lines: \"subs\", total lines: \"loc }'", stdin=subprocess.PIPE, shell=True)

print(results.decode('utf-8'))

第二种方法是shell=False

cmd1 = ['git', 'log', '--pretty=tformat:', '--numstat']
cmd2 = ['awk', '{ add += $1; subs += $2; loc += $1 - $2} END { print \"added lines: \"add\", removed lines: \"subs\", total lines: \"loc }']

p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE, shell=False)
p2 = subprocess.Popen(cmd2, stdin=p1.stdout, stdout=subprocess.PIPE, shell=False)
results = p2.communicate()[0].decode()

print(results)

文档建议尽可能使用subprocess.run,因此另一种选择是:

cmd1 = ['git', 'log', '--pretty=tformat:', '--numstat']
cmd2 = ['awk', '{ add += $1; subs += $2; loc += $1 - $2} END { print \"added lines: \"add\", removed lines: \"subs\", total lines: \"loc }']

p1 = subprocess.run(cmd1, stdout=subprocess.PIPE, shell=False)
p2 = subprocess.run(cmd2, input=p1.stdout, stdout=subprocess.PIPE, shell=False)

print(p2.stdout.decode('utf-8'))

shell=False是默认的,所以可以被删除。现在我可以通过按下一个按钮并输出结果来从Django web框架运行git命令。

相关问题