管道后如何在子壳内使用while循环

时间:2019-03-29 21:32:31

标签: bash shell awk sed

我已经寻找并找到了解决方案,但是找不到任何直接解决我的问题的方法。我正在尝试将某些作者在git repo中添加和删除的行加起来。我正在使用git log通过管道传输到sed,通过管道传输到awk,现在我试图通过管道传输到子外壳以将数字相加。问题在于,在子shell中无法正确解释管道输入,并且我不知道为什么。我怀疑它处于while循环中,因为subshel​​l语法的性质以及对分号的挑剔。

我在子shell内移动了代码,添加和删除了分号,使用反斜杠进行行分隔以查看是否是问题所在,但没有一个起作用。我不太精通Shell,因此对于经验丰富的人来说,这可能是一个显而易见的问题。 “ $ author”只是命令行中的第n个位置参数。

for author; do
        echo "Listing file and line changes for $author"
        git log --shortstat --author="$author" ${date:+--since="$date"} \
        | sed -n -e '/files\? changed/s/, /\n/gp' \
        | awk '
            $3=="changed"       {changed+=$1}
            $2=="deletions(-)"  {deletions+=$1}
            $2=="insertions(+)" {insertions+=$1}
            END{
                print "files changed:", changed,
                    " lines removed:", deletions,
                    " lines added:", insertions,
                    " net change:", insertions-deletions
            }'
done | {
      total_changed=0
      total_added=0
      total_removed=0
      while read changed insertions deletions; do
        let total_changed+=changed
        let total_added+=insertions
        let total_removed+=deletions
      done
      echo "totals:"
      echo "files changed: $total_changed"
      echo "lines added: $total_added"
      echo "lines removed: $total_removed" ;
    }

最后一部分应该输出总计,但输出0。我还会遇到一些奇怪的语法错误。这是输出(输入是“ Benjamin Hills”):

    /home/bhills/./git-log-lines-removed.sh: line 65: let: and line changes for Benjamin Hills: syntax error in expression (error token is "line changes for Benjamin Hills")
    /home/bhills/./git-log-lines-removed.sh: line 64: let: changed:: syntax error in expression (error token is ":")
    /home/bhills/./git-log-lines-removed.sh: line 65: let: 61  lines removed: 1345  lines added: 246  net change: -1099: syntax error in expression (error token is "lines removed: 1345  lines added: 246  net change: -1099")
    totals:
    files changed: 0
    lines added: 0
    lines removed: 0

1 个答案:

答案 0 :(得分:0)

您的代码正在尝试生成人类可读的输出两次:一次进入awk,另一次进入bash。由于awk生成的输出是输入到bash的,这意味着您试图通过管道传递给人类的输出格式,并像对待机器的输入格式一样读取它。显然不是。

完全没有理由采用这种方法:在同一过程中生成人类可读的输出 all

#!/usr/bin/env bash
#              ^^^^- NOT /bin/sh

total_changed=0
total_deletions=0
total_insertions=0

for author; do
  changed=0; deletions=0; insertions=0

  # Loop over output from "git log" for a single author
  while read added deleted _; do
    (( ++changed )) # each line is a file changed
    { [[ $added = - ]] || [[ $deleted = - ]]; } && continue # skip binary files
    (( insertions += added ))
    (( deletions += deleted ))
  done < <(git log --numstat --format='' --author="$author" ${date:+--since="$date"})

  # Print results from that author
  printf '%s\n' "For author: $author" \
                "  Files changed: $changed" \
                "  Deletions: $deletions" \
                "  Insertions: $insertions"

  # Add to totals
  (( total_changed+=changed ))
  (( total_deletions+=deletions ))
  (( total_insertions+=insertions ))
done

# Print those totals
printf '%s\n' "Totals:" \
              "  Files changed: $total_changed" \
              "  Deletions: $total_deletions" \
              "  Insertions: $total_insertions"