파이프 또는 명령줄 인수에서 파일 이름을 읽는 Bash 스크립트?

파이프 또는 명령줄 인수에서 파일 이름을 읽는 Bash 스크립트?

내 스크립트가 glob 또는 STDIN(공백 포함)으로 제공된 여러 파일 이름을 읽고 이를 사용하여 작업을 수행하기를 원합니다. 두 가지 방법을 별도로 읽을 수는 있지만 결합할 수는 없습니다.

이는 명령줄에서 glob을 읽습니다.

for filename in "$@"; do
    process_file "$filename"
done

이는 STDIN에서 읽습니다.

IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}"; do
    process_file "$filename"
done

for filename in...내가 정말로 원하는 것은 이들 중 하나를 배열로 읽어서 전체 루프를 두 번 반복할 필요가 없도록 하는 것입니다 . 이것이 명백하다면 죄송합니다. 저는 BASH를 처음 사용합니다.

편집: 가장 좋은 방법은 주어진 매개변수에서 이를 읽고 그렇지 않은 경우 기다리는 것입니다 STDIN. 어떻게 해야 합니까?

편집: 좋습니다. 문제는 제가 생각했던 것과 다릅니다. 문제는 process_file에도 사용자 입력이 필요하다는 것입니다. STDINEOF에서 읽기를 시작하고 저장한 다음 다시 입력을 요청하는 방법이 있습니까 ?

답변1

텍스트 처리 도구는 일반적으로 명령줄에 파일 이름을 지정하지 않으면 표준 입력에서 입력을 읽습니다. 변수를 테스트하여 명령줄 인수가 있는지 확인할 수 있습니다 $#. process_file표준 입력에 인수가 전달되지 않으면 표준 입력에서 읽는다고 가정합니다 .

if [ $# -eq 0 ]; then
  process_file
else
  for x; do
    process_file "$x"
  done
fi

또는 process_file인수가 필요한 경우 이를 전달하여 /dev/stdin표준 입력에서 읽어 보십시오.

if [ $# -eq 0 ]; then set /dev/stdin; fi
for x; do
  process_file "$x"
done

명령줄 인수 없이 표준 입력에서 읽을 파일 이름 목록을 요청하고 있습니다. 물론 가능하지만 특이하기 때문에 추천하지 않습니다. 사용자는 대부분의 다른 도구와는 다른 규칙을 배워야 합니다. 그럼에도 불구하고 다음과 같은 방법이 있습니다.

if [ $# -eq 0 ]; then
  set -f # turn off globbing
  IFS='
' # set newlines to be the only field separator
  set -- $(cat)
  set +f; unset IFS
fi
for x; do
  process_file "$x"
done

답변2

새로운 답변 2021-10-18

bash: 다음에서 목록을 생성합니다.토론 그리고/또는 표준 입력

다음 작은 스크립트를 고려해보세요.

#!/bin/bash

declare -a ARGS=("$@")

while read -rt .02 arg;do
    ARGS+=($arg)
    done

declare -p ARGS

전화해서 argsAndInput.sh샘플 요청도 가능하고...

seq 1 10 | ./argsAndInput.sh foo bar baz
declare -a ARGS=([0]="foo" [1]="bar" [2]="baz" [3]="1" [4]="2" [5]="3" [6]="4" [
7]="5" [8]="6" [9]="7" [10]="8" [11]="9" [12]="10")

seq 1 10 | ./argsAndInput.sh 
declare -a ARGS=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8"
 [8]="9" [9]="10")

./argsAndInput.sh foo bar baz 
declare -a ARGS=([0]="foo" [1]="bar" [2]="baz")

./argsAndInput.sh 
declare -a ARGS=()

필요에 따라 제한시간( read -t .02)을 줄이거나 다음을 사용할 수 있습니다.

while read -t 0 &&
    read -r arg;do
        ARGS+=($arg)
done

입력이 준비되었다고 확신하는 경우앞으로배쉬가 실행되어야 합니다.

오래된 답변

당신은 xargs그것을 사용할 수 있습니다전환하다 STDIN(항목이 많아도 명령줄 버퍼보다 ​​훨씬 큽니다)논쟁.

아니면 다음과 같은 것일 수도 있습니다.

if [ $# -gt 0 ] ;then
    for filename in  "$@" ; do
        process_file "$filename"
    done
  else
    IFS=$'\n' read -d '' -r -a filenames
    for filename in "${filenames[@]}"; do
        process_file "$filename"
    done
  fi

아니면 둘다:

  IFS=$'\n' read -d '' -r -a filenames
  for filename in "${filenames[@]}" "$@" ; do
        process_file "$filename"
    done
  fi

아니면 이런거 추가해서옵션, -l대표하는 것처럼행 매개변수만, 다음에서 읽지(대기) 않도록 하세요 STDIN.

case "$1" in
    -l )
        shift
        for filename in "$@" ; do
            process_file "$filename"
        done
        ;;
     * )
        for filename in "${filenames[@]}" "$@" ; do
            process_file "$filename"
        done
        ;;
    esac

(검증되지 않은!)

답변3

이것은 cygwin X11 gvim 및 Windows gvim에서 파일을 안전하게 열기 위해 작성한 스크립트입니다. 이는 쉘 래퍼로 설계되었으며 원하는 작업을 수행하는 부분입니다.

if [ ${#} -ne 0 ]; then #if we have filenames as CLArgs...
    [[ ! -z ${DEBUG} ]] && echo "Got arguments [${#}]:'${@}'"
    for OFILE in "${@}"; do
        [[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
        [[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
        echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
        "${VIMRUN}" --servername GVIM $RT "${OFILE}" &
        RT="--remote-tab"
    done
else #otherwise read from stdin safely and handle spaces...
    while read OFILE; do
        [[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
        [[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
        echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
        "${VIMRUN}" --servername GVIM $RT "${OFILE}" &
        RT="--remote-tab"
    done
fi

답변4

이상 bash4.4:

if [ "$#" -eq 0 ]; then
  readarray -d '' args
  set -- "${args[@]}"
fi

for arg do
   something with "$arg"
done

관련 정보