Bash

如何使用文件名參數或預設為標準輸入、標準輸出(簡要)

  • October 11, 2013

我想以更簡潔、更靈活的方式將文件名作為 bash 腳本中的參數處理,輸入和輸出文件名採用 0、1 或 2 個參數。

  • 當 args = 0 時,從標準輸入讀取,寫入標準輸出
  • 當 args = 1 時,從 $1 讀取,寫入 stdout
  • 當 args = 2 時,從 $ 1, write to $ 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

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 "$_"; } }

我會更多地使用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

解釋

  • [[ $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到先前設置的文件描述符34分別。

重新打開標準輸入和輸出

如果您不想打開中間文件描述符,另一種方法是直接用指定的輸入和輸出文件stdin替換對應的文件描述符:stdout

#!/bin/bash
[[ $1 ]] && [[ ! -f $1 ]] && echo "file $1 dne" && exit 1
[[ $1 ]] && exec 0<$1
[[ $2 ]] && exec 1>$2
fgrep -v "stuff"

這種方法的一個缺點是您失去了將腳本本身的輸出與作為重定向目標的命令的輸出區分開來的能力。在原始方法中,您可以將腳本輸出定向到未修改的stdinand stdout,而後者又可能被腳本的呼叫者重定向。指定的輸入和輸出文件仍然可以通過相應的文件描述符訪問,這與腳本stdinstdout.

引用自:https://unix.stackexchange.com/questions/94224