【问题标题】:How does process substitution work with while loops?进程替换如何与 while 循环一起工作?
【发布时间】:2020-05-09 20:24:25
【问题描述】:

我正在阅读/编辑一个 bash git 集成脚本

这个 sn-p 应该打印 ${SYMBOL_GIT_PUSH}${SYMBOL_GIT_PULL} 以及我落后和/或领先的提交数。

local marks
while IFS= read -r line; do
    if [[ $line =~ ^## ]]; then
        [[ $line =~ ahead\ ([0-9]+) ]] && marks+=" ${BASH_REMATCH[1]}${SYMBOL_GIT_PUSH}"
        [[ $line =~ behind\ ([0-9]+) ]] && marks+=" ${BASH_REMATCH[1]}${SYMBOL_GIT_PULL}"
    else
        marks="${SYMBOL_GIT_MODIFIED}${marks}"
        break
    fi
done < <(git status --porcelain --branch 2>/dev/null)
printf '%s' "$marks"

例子:

 4↑ 10↓

它正在工作,但我正在努力理解它。

为什么会有一些IFS 以及它如何与进程替换一起工作?
我听说sh 中没有定义流程。有没有办法以/bin/sh 方式或至少更有效地做到这一点?

我得到了a link,它应该可以解释IFS 的作用。


我切换了混淆的东西并设法删除了进程替换:

local marks
git status --porcelain --branch 2>/dev/null |
while IFS= read -r line; do
    if [[ $line =~ ^## ]]; then
        [[ $line =~ ahead\ ([0-9]+) ]] && marks+=" ${BASH_REMATCH[1]}${SYMBOL_GIT_PUSH}"
        [[ $line =~ behind\ ([0-9]+) ]] && marks+=" ${BASH_REMATCH[1]}${SYMBOL_GIT_PULL}"
    else
        marks="${SYMBOL_GIT_MODIFIED}${marks}"
        break
    fi
done
printf '%s\n' "$marks"

但是现在,$marks 的值没有保存,它什么也不打印。
我收到了another link,这解释了原因。
将返回并更新我的发现。


我使用命令分组解决方法并将循环和打印语句包裹在花括号内:

此外,我使 /bin/sh 版本几乎可以正常工作(例外 - 显示我领先或落后的提交量,不难,我相信我会用 awk 或 cut 做一些事情)。
我利用了 grep 在没有匹配项时返回非 0 的事实。

git status --porcelain --branch 2>/dev/null | {
    SYMBOL_GIT_PUSH='↑'
    SYMBOL_GIT_PULL='↓'
    while IFS= read -r line
    do
        if echo "$line" | egrep -q '^##'
        then
            echo "$line" | egrep -q 'ahead' && marks="$marks $SYMBOL_GIT_PUSH"
            echo "$line" | egrep -q 'behind' && marks="$marks $SYMBOL_GIT_PULL"
        else
            marks="*$marks"
            break
        fi
    done
    printf ' %s' "$marks"
}

这是一次有趣的学习经历!感谢所有帮助过的人。当我找到 100% 的解决方案时,我会更新它。

【问题讨论】:

  • 那么什么不起作用?你想改进什么?
  • 将您的代码放入脚本中,并将#!/bin/sh 添加为shebang,然后将其粘贴到shellcheck.net,这应该会给您一些帮助。 sh 也没有正则表达式,只有 glob,但如果你真的需要正则表达式,那么你需要使用外部实用程序,如 grepsedawk 和朋友。
  • 关于进程替换,当它与重定向结合时 - 就像在你的例子中一样 - command … &lt; &lt;(command2 …) 可以用一个简单的管道重写,command2 … | command …
  • 要了解IFS 的含义,请参阅:mywiki.wooledge.org/IFS
  • 用管道替换有一个重要的区别:命令在子shell中运行,请参阅BashFAQ/024。对于问题中的示例,marks 最后会为空,所以这里肯定不等价。

标签: bash pipe pipeline


【解决方案1】:

这是无 bashism 的 git info 函数。

__git() {
    git_eng="env LANG=C git"
    ref="$($git_eng symbolic-ref --short HEAD 2>/dev/null)"
    [ -n "$ref" ] && ref="$SYMBOL_GIT_BRANCH$ref" || ref="$($git_eng describe --tags --always 2>/dev/null)"
    [ -n "$ref" ] || return;

    git status --porcelain --branch 2>/dev/null | {
        SYMBOL_GIT_PUSH='↑'
        SYMBOL_GIT_PULL='↓'
        while IFS= read -r line
        do
            if echo "$line" | grep -E -q '^##'
            then
                echo "$line" | grep -E -q 'ahead' &&
                    marks="$marks $SYMBOL_GIT_PUSH$(echo "$line" | sed 's/.*\[ahead //g' | sed 's/\].*//g')"
                echo "$line" | grep -E -q 'behind' &&
                    marks="$marks $SYMBOL_GIT_PULL$(echo "$line" | sed 's/.*\[behind //g' | sed 's/\].*//g')"
            else
                marks="$SYMBOL_GIT_MODIFIED$marks"
                break
            fi
        done
        printf ' %s%s' "$ref" "$marks"
    }
}

sed 搜索 [ahead 并删除它,以及它之前的所有内容,然后将其通过管道传送到另一个 sed,这将删除 ] 之后的所有内容。这样就只剩下数字了。

【讨论】:

    猜你喜欢
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-27
    • 2021-07-27
    • 1970-01-01
    • 2018-04-03
    • 2011-08-06
    相关资源
    最近更新 更多