파일 이름 매개변수를 사용하거나 stdin, stdout을 기본값으로 사용하는 방법(간단)

파일 이름 매개변수를 사용하거나 stdin, stdout을 기본값으로 사용하는 방법(간단)

bash 스크립트에서 파일 이름을 매개 변수로 처리하고 0, 1 또는 2개의 매개 변수를 입력 및 출력 파일 이름으로 사용하는 더 명확하고 유연한 방법을 원합니다.

  • args = 0이면 stdin에서 읽고 stdout에 씁니다.
  • args = 1이면 $1에서 읽고 stdout에 씁니다.
  • args = 2이면 $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

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

답변1

더 많이 사용하겠습니다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각각 으로 리디렉션합니다 .stdout34

표준 입력 및 출력 다시 열기

중간 파일 설명자를 열지 않으려는 경우 대안은 해당 stdin파일 설명자를 지정된 입력 및 출력 파일로 직접 바꾸는 것입니다 stdout.

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

이 접근 방식의 단점은 스크립트 자체의 출력과 리디렉션 대상인 명령의 출력을 구별할 수 없다는 것입니다. 원래 방법에서는 스크립트 출력을 수정되지 않은 stdinsum 으로 지정할 수 있었으며 stdout, 이는 결국 스크립트 호출자에 의해 리디렉션되었을 수 있습니다. 지정된 입력 및 출력 파일은 스크립트 stdinstdout.

답변2

어때요?

  input="${1:-/dev/stdin}"
  output="${2:-/dev/stdout}"
  err="${3:-/dev/stderr}"

  foobar <"$input" >"$output" 2>"$err"

주의하셔야 할 점 /dev/std(in|out|err)POSIX 표준에는 없습니다.따라서 이는 이러한 특수 파일을 지원하는 시스템에서만 작동합니다.

이는 또한 입력이 정상이라고 가정합니다. 리디렉션하기 전에 파일이 존재하는지 확인하지 않습니다.

답변3

마음에 들지 않으면 출력은 다음과 같습니다.언제나stdout으로 리디렉션하려면 다음 줄을 사용할 수 있습니다.

cat $1 |fgrep -v "stuff" | tee  

답변4

이것이 "더 깔끔한" 것인지는 모르겠지만 여기에 몇 가지 제안 사항이 있습니다(이것은 테스트된 코드가 아닙니다). 사용 exec(Thomas Nyman에 따르면)은 안전 문제를 일으킬 수 있으므로 주의해서 다루어야 합니다.

반복되는 코드를 함수에 배치하는 것부터 시작하세요.

# die <message>
function die(){
    echo "Fatal error: $1, exiting ..." >&2
    exit 1
}

# is_file <file-path>
function is_file(){
    [[ -n "$1" && -f "$1" ]] && return 0
    die 'file not found'
}

여기서는 용도가 아니라 fgrep친구 cat입니다. 그런 다음 선택 사례를 사용하십시오.

case $# in
    0) cat ;;                                  # accepts stdin to stdout.
    1) is_file "$1"; cat "$1" ;;               # puts $1 to stdout.
    2) is_file "$1"; cat "$1" > "$2" ;;        # puts $1 to $2.
    *) die 'too many arguments' ;;
esac

또 다른 옵션(깨끗하고 매우 컴팩트함)은 명령어를 배열에 로드한 다음 $# 값(함수 포인터와 유사)을 통해 액세스하는 것입니다. 위의 함수를 고려하면 is_fileBash 코드는 다음과 같습니다.

# action array.
readonly do_stuff=(
    'cat'                                  # 0 arg.
    'is_file \"$1\"; cat \"$1\"'           # 1 arg.
    'is_file \"$1\"; cat \"$1\" > \"$2\";' # 2 args.
)

# Main - just do:
[[ $# -le 2 ]] && ${do_stuff[$#]} || die 'too many arguments' 

구문에 100% 익숙하지는 않지만 큰따옴표를 이스케이프해야 합니다. 파일 경로 변수를 큰따옴표로 묶는 것이 가장 좋습니다.

추가하자면, $2를 쓸 때 파일이 존재하지 않는지 확인해야 합니다. 그렇지 않으면 덮어쓰게 됩니다.

관련 정보