내 스크립트는 다음과 같이 작동합니다.
./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
셸을 사용하여 표준 규칙을 따르는 옵션을 처리할 수 있습니다.
getopt
Linux에서는 표준 옵션을 처리하기 위해 유틸리티를 사용할 수도 있고 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 file2
regexp는 이러한 파일에서 검색되지만 grep -e regexp
stdin에서는 검색되지 않습니다.
매개변수나 표준 입력을 통해 입력이 제공되도록 하려면 다음을 수행해야 합니다.언제나표준 입력에서 읽기:
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/null
stdin 이 아닌 다른 파일로 리디렉션되는 경우 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
하여 호출됩니다 . 그러나 목록이 빙빙되면 여러 호출로 분할될 수 있으며, 각 호출은 에 있는 행의 하위 집합을 가져오지만 각 호출은 합계 도 가져옵니다 .file2
filelist.txt
myscript
filelist.txt
file1
file2
답변2
파이프가 없을 때 입력을 기다리지 않으려면 읽기 전에 테스트하십시오.
[ -p /dev/stdin ] && read PIPEIN
Ubuntu Bash 5에서 테스트되었습니다.