Bash 스크립트에서 파이프된 stdin 및 위치 인수를 처리하기 위한 일반 함수

Bash 스크립트에서 파이프된 stdin 및 위치 인수를 처리하기 위한 일반 함수

내 스크립트는 다음과 같이 작동합니다.

./script file1 file2 file3 ... -x -st -i

또는

cat filelist.txt | ./script -x -st -i

또는

./script <<< "$(cat filelist) -x -st -i

파일이 위치 매개변수나 파이프 또는 리디렉션에 의해 제공되는 경우 파일 이름에서 배열을 설정하는 일반적인 기능에 대해 누군가 도움을 줄 수 있습니까?

단일 쉘 스크립트에서 위의 모든 상황을 어떻게 처리할 수 있습니까?

시험을 마친:

input="$(</dev/stdin)"
Arr_tmp+=("$@")
if ! echo "${Arr_tmp[@]}" | grep 'file_regex'; then  Array+=( $(echo "$input") ); else Array+=("$(echo "$@" | grep 'file_regex')"); fi

또는


input="$(</dev/stdin)"
IFS=$'\n' read -ra Array -d '' <<< "$(echo $input)"
if [[ "${#Array[@]}" == 0 ]]; then 
Arr_tmp+=("$@")
fi

하지만 아무것도 작동하지 않습니다

위의 문제는 표준 입력이 파이프되지 않으면 읽는 동안 스크립트가 작동하지 않는다는 것입니다. 표준 입력이 파이프되지 않은 경우 스크립트가 매개변수를 읽고 해당 매개변수로 돌아가는 것을 방지할 수 있는 방법이 있습니까?

답변1

첫째, 옵션이 아닌 인수 다음에 옵션을 기대하는 것은 나쁜 습관입니다.

표준 명령줄 인터페이스는 옵션이 아닌 인수 앞에 옵션을 두고 --다음으로 시작하는 옵션이 아닌 인수를 허용할 수 있도록 옵션 끝에 옵션 표시를 허용하는 것입니다 -.

myscript -c -ofile.out -- -file1-.in -file2-.in

그것은 다음과 같습니다:

myscript -co file.out -- -file1-.in -file2-.in

예를 들어, -o인수가 있는 옵션과 -c인수가 없는 옵션은 --옵션의 끝을 표시하는 반면, -file1-.in는 옵션이 아닌 두 개의 인수로, 인수로 시작하더라도 옵션의 끝 뒤에 오기 때문에 -file2-.in여전히 그렇게 처리됩니다. 논쟁. .---

내장된 표준 getopts셸을 사용하여 표준 규칙을 따르는 옵션을 처리할 수 있습니다.

getoptLinux에서는 표준 옵션을 처리하기 위해 유틸리티를 사용할 수도 있고 util-linux, GNU 스타일의 긴 옵션이나 선택적 인수가 있는 옵션을 사용할 수도 있고, 옵션이 아닌 인수 뒤에 옵션이 사용되도록 허용할 수도 있습니다(환경 제외 ) $POSIXLY_CORRECT. --여전히 존경받고 있습니다. getopt옵션이 아닌 옵션 이전에 옵션이 있는 인수를 적절하게 재정렬하는 작업을 처리합니다.

명령줄이나 표준 입력을 통해 제공된 입력을 처리하려면 다음을 수행해야 합니다.

while getopts...; do
  # process options
  ...
done
# option processing done.
shift "$((OPTIND - 1))"
# now the positional parameters contain non-option arguments

if (( $# )); then
  args=("$@")
else
  # no non-option arguments, read them one per line from stdin
  readarray -t args
fi

그것은 ((...))일종의 크시즘(역시 지원됨 bash), readarray일종의 바시즘이다. 에서는 sh다음을 수행합니다.

if [ "$#" -eq 0 ]; then
  # no non-option arguments, read them from stdin
  IFS='
' # split on newline
  set -o noglob
  set -- $(cat) # read and split+glob with glob disabled
fi

결과는 위의 bash 예제와 같이 배열 대신 위치 인수( $1, $2... )에 저장됩니다. 이 경우 빈 줄은 삭제됩니다."$@"$args

또한 파일 경로에는 0을 제외한 모든 바이트 값이 포함될 수 있고 개행 문자도 포함될 수 있으므로 한 줄에 임의의 파일 경로 목록을 전달할 수 없습니다.

위에서는 명령줄에 옵션이 아닌 인수가 전달되지 않은 경우에만 표준 입력에서 준비합니다. 이것이 대부분의 텍스트 유틸리티가 수행하는 작업입니다. 예를 들어, grep -e regexp -- file1 file2regexp는 이러한 파일에서 검색되지만 grep -e regexpstdin에서는 검색되지 않습니다.

매개변수나 표준 입력을 통해 입력이 제공되도록 하려면 다음을 수행해야 합니다.언제나표준 입력에서 읽기:

readarray -t args
args+=("$@")

두 개를 로 결합합니다 $args.

사용자가 표준 입력을 통해 아무것도 전달하고 싶지 않은 경우 언제든지 다음을 수행할 수 있습니다.

myscript arg1 arg2 < /dev/null

stdin이 터미널인 경우 목록을 대화형으로 전달하려면 파일 목록을 입력하고 Ctrl+ 로 끝냅니다 d.

다음을 수행하고 싶을 수도 있습니다.

if [ -t 0 ]; then
  args=()
else
  readarray -t args
fi
args+=("$@")

tty 장치가 아닌 경우에만 stdin을 읽습니다. 그러나 이는 사용자가 스크립트를 무시하기 쉽다는 것은 나쁜 생각이라고 생각합니다.가능한/dev/nullstdin 이 아닌 다른 파일로 리디렉션되는 경우 stdin을 사용하십시오 while IFS= read -r...; do myscript ...; done < some-file.

@ilkkachu가 제안한 것처럼 표준 입력이나 임의 파일에서 입력을 가져오는 옵션을 추가하여 사용자가 표준 입력을 읽을 시기와 읽지 않을 시기를 결정할 수도 있습니다.

xargs또 다른 옵션은 입력 스트림을 매개변수 목록으로 변환하는 도구를 사용하는 것입니다 . 예를 들어 GNU를 사용하면 다음과 같습니다 xargs.

xargs -d '\n' -a filelist.txt myscript -x -st -i file1 file2

myscript와 각 줄의 내용을 매개변수로 사용 file1하여 호출됩니다 . 그러나 목록이 빙빙되면 여러 호출로 분할될 수 있으며, 각 호출은 에 있는 행의 하위 집합을 가져오지만 각 호출은 합계 도 가져옵니다 .file2filelist.txtmyscriptfilelist.txtfile1file2

답변2

파이프가 없을 때 입력을 기다리지 않으려면 읽기 전에 테스트하십시오.

[ -p /dev/stdin ] && read PIPEIN

Ubuntu Bash 5에서 테스트되었습니다.

관련 정보