Chu*_*ill 5 bash stdout stdin arguments
我想以更简洁、更灵活的方式将文件名作为 bash 脚本中的参数处理,为输入和输出文件名采用 0、1 或 2 个参数。
如何使 bash 脚本版本更简洁、更短?
这是我现在拥有的,有效,但不干净,
#!/bin/bash
if [ $# -eq 0 ] ; then #echo "args 0"
fgrep -v "stuff"
elif [ $# -eq 1 ] ; then #echo "args 1"
f1=${1:-"null"}
if [ ! -f $f1 ]; then echo "file $f1 dne"; exit 1; fi
fgrep -v "stuff" $f1
elif [ $# -eq 2 ]; then #echo "args 2"
f1=${1:-"null"}
if [ ! -f $f1 ]; then echo "file $f1 dne"; exit 1; fi
f2=${2:-"null"}
fgrep -v "stuff" $f1 > $f2
fi
Run Code Online (Sandbox Code Playgroud)
perl 版本更干净,
#!/bin/env perl
use strict;
use warnings;
my $f1=$ARGV[0]||"-";
my $f2=$ARGV[1]||"-";
my ($fh, $ofh);
open($fh,"<$f1") or die "file $f1 failed";
open($ofh,">$f2") or die "file $f2 failed";
while(<$fh>) { if( !($_ =~ /stuff/) ) { print $ofh "$_"; } }
Run Code Online (Sandbox Code Playgroud)
我会更多地使用I/O 重定向:
#!/bin/bash
[[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1
[[ $1 ]] && exec 3<$1 || exec 3<&0
[[ $2 ]] && exec 4>$2 || exec 4>&1
fgrep -v "stuff" <&3 >&4
Run Code Online (Sandbox Code Playgroud)
解释
[[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1
测试输入文件是否已指定为命令行参数以及该文件是否存在。
[[ $1 ]] && exec 3<$1 || exec 3<&0
如果$1设置,即已指定输入文件,则指定文件在文件描述符处打开3,否则stdin在文件描述符处重复3。
[[ $2 ]] && exec 4>$2 || exec 4>&1
类似地,如果$2设置了 ,即已指定输出文件,则指定的文件在文件描述符处打开4,否则stdout在文件描述符处复制4。
fgrep -v "stuff" <&3 >&4
最后fgrep被调用时,其重定向stdin和stdout以前设置的文件描述符3和4分别。
重新打开标准输入和输出
如果您不希望打开中间的文件描述符,另一种是更换对应的文件描述符stdin,并stdout直接与指定的输入和输出文件:
#!/bin/bash
[[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1
[[ $1 ]] && exec 0<$1
[[ $2 ]] && exec 1>$2
fgrep -v "stuff"
Run Code Online (Sandbox Code Playgroud)
这种方法的一个缺点是您无法将脚本本身的输出与作为重定向目标的命令的输出区分开来。在原始方法中,您可以将脚本输出定向到未修改的stdinand stdout,而后者可能已被脚本调用者重定向。指定的输入和输出文件仍然可以通过相应的文件描述符访问,这与脚本stdin和stdout.