使用文件作为输入或stdin运行命令的最佳方法

时间:2014-10-06 22:27:09

标签: bash awk

我正在编写一个脚本来格式化文件,使每列的宽度为其最长记录的长度+ 1。该脚本可以正常运行./auto_format filecat file | ./auto_format

#!/bin/bash

# auto_format file 

case $# in
  1)
    file="$1"
    ;;
  0)
    file=$(mktemp || echo "failed, exiting..." 1>&2; exit 1)
    cat > $file <&0
    ;;
  *)
    echo "usage: auto_format [file]" 1>&2
    exit 1
    ;;
esac

awk ' 
NR==FNR {
for (i=1;i<=NF;i++) {
  if (length($i) > max[i]) max[i]=length($i);
  }
}
NR!=FNR {
for (i=1;i<=NF;i++){
  printf "%-*s", max[i]+1, $i 
}
printf "\n"
  }
  ' "$file" "$file"

但是,我不喜欢在接收来自STDIN的输入时使用临时文件,并且想知道我是否可以将输入的副本传递给awk,所以我不必使用临时文件。类似的东西:awk [script] STDIN COPY_STDIN

2 个答案:

答案 0 :(得分:2)

处理此问题的一个好方法是,如果提供了该文件,则从文件重定向stdin:

if [ -n "$1" ]; then exec <"$1"; fi

当且仅当提供了文件名时,这将在您的第一个参数中打开文件,替换stdin。


那就是说,你的具体案例比较棘手,你需要捕获内容,因为你想要两次返回用户的输入。但是,您不一定需要捕获到文件 - 捕获到变量,并将该变量重新播放两次,这样做。如果您的内容不包含NUL,那么就像以下一样简单:

#!/bin/bash
# ^- this will not work with /bin/sh

if [ -n "$1" ]; then exec <"$1"; fi

IFS= read -r -d '' content
awk ... <(printf '%s' "$content") <(printf '%s' "$content")

如果您的内容 包含NUL,那么通过将内容存储在数组而不是标量变量中仍然可以实现解决方案(因为POSIX shell使用C风格的NUL终止字符串,标量可以'包含一个NUL - 但是数组条目之间的划分可以代表NULs的位置),但角落的情况有点毛茸茸;坦率地说,在这一点上使用临时文件(或像Python一样使用Pascal字符串,而不是NUL分隔的语言)会更容易。

答案 1 :(得分:2)

看起来你正在努力使这比以前更难。 awk完全有能力处理管道标准输入或文件,你不需要一个tmp文件,除非你的输入是巨大的,听起来它不是你的评论::

$ cat tst.sh
awk '
{ 
    for (i=1;i<=NF;i++) {
        if (length($i) > max[i]) max[i]=length($i);
    }
    line[NR] = $0
}
END {
    for (nr=1; nr<=NR; nr++) {
        nf = split(line[nr],flds)
        for (i=1; i<=nf; i++) {
            printf "%-*s", max[i]+1, flds[i]
        }
        print ""
    }
}
' "$@"

$ cat file
abc de fghi
abcde f ghiklm
$ 
$ ./tst.sh file
abc   de fghi   
abcde f  ghiklm 
$ 
$ cat file | ./tst.sh
abc   de fghi   
abcde f  ghiklm