单条Git提交时Bash脚本while循环失效问题排查
单条提交时Bash脚本无法添加前缀的问题解决
问题重现
你编写的Bash脚本用于生成变更日志,逻辑是获取上一个符合tag_prefix*规则的标签之后的所有非合并提交,给每条提交消息添加- 前缀。当存在多条提交时输出符合预期,但仅单条提交时,脚本输出为空,预期应该是- commit_message_1。
脚本代码:
#!/bin/bash tag_commits_reversed=$(git rev-list --tags --max-count=1) tags=$(git describe --match "tag_prefix*" --abbrev=0 --tags $tag_commits_reversed) commit_messages=$(git log $tags..@ --no-merges --pretty=format:"%s" -- . | while read line; do echo "- $line"; done) echo $commit_messages
问题原因
核心问题出在read命令的行为和git log的输出格式上:
read的行结束规则:read默认以换行符作为行的结束标记,当git log仅输出单条提交消息时,--pretty=format:"%s"的结果没有尾随换行符。此时管道输入的内容没有换行,read在读取时会因为没有遇到换行标记,在管道关闭时返回非零退出状态,导致while循环根本不会执行,自然没有输出。- 另外,
echo $commit_messages的写法会把变量中的换行符替换为空格,多条提交时看似正常,实则破坏了每行独立的结构,应该改为echo "$commit_messages"来保留换行。
解决方案
有两种简洁的修复方式:
方式一:修复read的循环逻辑
给read添加-r参数(避免反斜杠被转义),同时在循环条件中加入|| [[ -n $line ]],确保即使最后一行没有换行符,只要内容非空就会处理:
#!/bin/bash tag_commits_reversed=$(git rev-list --tags --max-count=1) tags=$(git describe --match "tag_prefix*" --abbrev=0 --tags $tag_commits_reversed) commit_messages=$(git log $tags..@ --no-merges --pretty=format:"%s" -- . | while read -r line || [[ -n $line ]]; do echo "- $line"; done) # 加双引号保留换行格式 echo "$commit_messages"
方式二:直接用git log生成带前缀的输出
完全不需要管道和循环,直接在git log的--pretty=format中指定前缀,这是更高效且可靠的方式:
#!/bin/bash tag_commits_reversed=$(git rev-list --tags --max-count=1) tags=$(git describe --match "tag_prefix*" --abbrev=0 --tags $tag_commits_reversed) # 直接在format中加入"- "前缀 commit_messages=$(git log $tags..@ --no-merges --pretty=format:"- %s" -- .) echo "$commit_messages"
这个方式从根源避免了管道和read的兼容性问题,不管是单条还是多条提交,都能正确生成带前缀的每行消息。
内容的提问来源于stack exchange,提问作者ProtocolGuy
相关产品推荐
相关产品推荐

