"2단계" 스크립트는 파일이나 표준 입력에서 입력 읽기를 어떻게 지원합니까?

"2단계" 스크립트는 파일이나 표준 입력에서 입력 읽기를 어떻게 지원합니까?

다음은 "두 번 스크립팅"이 의미하는 매우 간단한 예입니다.

#!/bin/bash

INPUTFILE=$1

grep    '^#' "$INPUTFILE"
grep -v '^#' "$INPUTFILE" | sort

스크립트(라고 부르겠습니다)는 twopass.sh파일 경로를 유일한 인수로 사용합니다. 그런 다음 먼저 로 시작하는 모든 줄을 원래 순서대로 인쇄합니다 INPUTFILE. 둘째, 인쇄됩니다.INPUTFILE#정렬순으로내부의 모든 라인이 INPUTFILE완료되었습니다.아니요첫 번째 #.

예를 들어 파일에 example.txt다음 줄이 포함되어 있는 경우

# foo comes first
# bar comes second
# baz comes third
wobble
quux
wibble
frobozz

...그런 다음 twopass.sh스크립트를 적용하면 다음과 같은 결과가 생성됩니다.

% ./twopass.sh example.txt
# foo comes first
# bar comes second
# baz comes third
frobozz
quux
wibble
wobble

이 스크립트를 수정하려면 어떻게 해야 합니까?반품stdin에 대해서도 같은 작업을 수행합니까?

즉, 필요한 새 버전의 스크립트를 사용하면 다음 줄은 위에 표시된 것과 동일한 출력을 생성해야 합니다.

./twopass.sh < example.txt

나는 이 질문에 대한 답변에 매우 bash관심이 있습니다 zsh.

답변1

일반적인 경우 stdin을 여러 번 처리하려면 첫 번째 읽기 후 다시 읽을 수 있도록 역추적할 수 있어야 합니다(파이프, 소켓, 터미널 등 모든 유형의 파일에서는 가능하지 않음). 또는 입력을 여러 번 읽을 수 있는 일반 파일이나 메모리에 저장하세요.

내장된 검색 및 임시 파일 관리 지원(예: zsh 또는 ksh93)이 있는 셸을 사용하는 것이 더 쉽습니다.

#! /bin/zsh -
zmodload zsh/system || exit

if (($#)); then
  # arguments are provided. They are assumed to be file arguments
  # to process (use ./- for the file called -)
  grep -h -- '^#' "$@"
  grep -vh -- '^#' "$@" | sort
else
  # process stdin
  if (( (pos = systell(0)) >= 0 )); then
    # input is seekable
    grep '^#'
    sysseek $pos || {
      syserror -p "Cannot go back: "
      exit 1
    }
    grep -v '^#' | sort
  else
    # not seekable, store input in a temporary file using =(cat)
    () {
      grep -- '^#' $1
      grep -v -- '^#' $1
    } =(cat)
  fi
fi

( -h출력 파일 이름을 건너뛰는 것은 GNU grep확장 이므로 grep지원하지 않는 경우 해당 확장으로 바꿀 수 있습니다 cat -- "$@" | grep ....)

bash임시 파일을 찾거나 생성하는 기능은 지원되지 않지만 , 또는 / 를 호출하도록 zshksh93perl있습니다 python.

그러나 특정 사용 사례의 경우 다음과 같이 수행할 수도 있습니다.

#! /bin/sh -
gawk -e '
  /^#/ {print; next}
  {print | "sort"}' -E /dev/null "$@"

-e+이 트릭을 -E사용하려면 문자가 포함된 파일 이름을 처리할 수 있어야 합니다 =( -인수는 여전히 gawk이름이 지정된 파일이 아니라 stdin을 의미하는 것으로 해석됩니다 -).

위에 정렬된 출력 표시 보장뒤쪽에sort읽어야 할 댓글모두출력을 시작하기 전에 입력합니다. sort데이터를 메모리나 임시 파일에 저장합니다.

아래와 같은 방법:

#! /bin/zsh -
{ cat -- "$@" > >(grep '^#' 4>&1 >&3) | grep -v '^#' | sort; } 3>&1

또는 ksh93 또는 bash와 호환됩니다.

{
  cat -- "$@" |
   { tee >(grep '^#' 4>&1 >&3); } |
   grep -v '^#' |
   sort
} 3>&1

출력은 둘 다에 적용 cat되며 작동해야 합니다 . 쓰기가 완료될 때까지 출력이 시작되지 않도록 보장 하는 데 사용됩니다 (런타임에도 파이프를 열어두기 때문).teegrepgrep -v | sort4>&1sortgrepgrep -v

답변2

sort정렬하려는 출력 부분 만 해당됩니다 .grep -E '^#' "$INPUTFILE";(grep -E -v '^#' "$INPUTFILE" | sort )

관련 정보