Bash 내장 "읽기"가 주석이나 빈 줄을 무시하도록 하려면 어떻게 해야 합니까?

Bash 내장 "읽기"가 주석이나 빈 줄을 무시하도록 하려면 어떻게 해야 합니까?

(단순화를 위해 읽을 파일이 첫 번째 인수인 것으로 가정합니다. $1)

난 내가 원하는 걸 할 수 있어외부적으로그리고:

tempfile=$(mktemp)
awk '/^#/ {next}; NF == 0 {next}; {print}' "$1" > $tempfile
while read var1 var2 var3 var4 < $tempfile; do
  # stuff with var1, etc.
done

awk그러나 구성 파일을 구문 분석할 때마다 이를 호출해야 한다는 것은 터무니없는 것 같습니다. read파일의 주석 줄이나 빈 줄을 무시 하는 방법이 있습니까 ?아니요외부 바이너리/잠재적인 성능 문제가 있습니까?


지금까지의 답변은 매우 도움이 됩니다! 명확히 말하면 임시 파일을 사용하고 싶지 않지만하다구성을 읽고 싶습니다.파일에서, 표준 입력이 아닙니다. 스크립트를 호출할 때 입력 리디렉션을 사용할 수 있다는 것을 잘 알고 있지만 여러 가지 이유로 제 경우에는 이것이 작동하지 않습니다.

읽을 입력을 소프트 인코딩하고 싶습니다. 예를 들면 다음과 같습니다.

configfile="/opt/myconfigfile.txt"
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

while read var1 var2 var3 var4 < "$configfile" ; do
  ...

configfile하지만 이것을 시도하면 프로세스가 종료될 때까지 첫 번째 줄을 계속해서 읽습니다.

어쩌면 이것은 그 자체의 질문이어야 할 수도 있지만... 내가 하고 있는 일에서 줄이 바뀔 수도 있습니다. 내 실수는 어디에 있습니까?

답변1

이를 수행하기 위해 임시 파일이 필요하지 않으며 sed(또는 awk)는 쉘 케이스 명령문보다 주석 처리에 훨씬 더 유연합니다.

예를 들어:

configfile='/opt/myconfigfile.txt'
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile" |
    while read var1 var2 var3 var4; do
      # stuff with var1, etc.
    done

# Note: var1 etc are not available to the script at this
# point. They are only available in the sub-shell running
# the while loop, and go away when that sub-shell ends.

이렇게 하면 주석(선행 공백 포함 또는 제외)이 제거되고 입력을 while 루프에 공급하기 전에 입력에서 빈 줄이 제거됩니다. 해당 줄의 주석과 줄 끝에 추가된 주석을 별도로 처리합니다.

# full-line comment
# var1 var2 var3 var4
abc 123 xyz def # comment here

이와 같은 전화 sed나 작업은 awk"어리석은" 일이 아니라 완전히 정상적인 일입니다. 이것이 바로 이 도구의 목적입니다. 성능에 관해서는 매우 작은 입력 파일을 제외하면 이 sed버전이 훨씬 더 빠를 것이라고 확신합니다. 파이핑에는 sed약간의 시작 오버헤드가 있지만 매우 빠르게 실행되는 반면 셸은 느립니다.


2022년 5월 3일에 업데이트됨:

while 읽기 루프(var1, var2, var3 등)에 설정된 변수는 while 루프가 끝나면 "범위를 벗어납니다". while 루프 내에서만 사용할 수 있습니다. while 루프는 구성 파일이 파이프로 연결되어 있기 때문에 서브셸에서 실행됩니다. 서브쉘이 죽으면 해당 환경과 하위 프로세스도 함께 사라집니다.할 수 없다상위 프로세스의 환경을 변경합니다.

while 루프 후에도 변수의 값이 유지되도록 하려면 파이프 사용을 피해야 합니다. 예를 들어 입력 리디렉션( <) 을 사용하고프로세스 교체( <(...)):

while read var1 var2 var3 var4; do
  # stuff with var1, etc.
done < <(sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile")

# remainder of script can use var1 etc if and as needed.

이 프로세스 대체 버전을 사용하면 while 루프가 상위 셸에서 실행되고 sed스크립트는 하위 프로세스로 실행됩니다(해당 출력은 while 루프로 리디렉션됨). sed와 해당 환경은 완료 시 사라지지만 while 루프를 실행하는 셸은 루프에 의해 생성/변경된 변수를 유지합니다.

답변2

이는 공백(IFS)의 모든 항목이 삭제되기 때문에 작동합니다 read. 따라서 var1이 비어 있거나 "#"으로 시작하면 건너뜁니다.

while read var1 var2 var3 var4; do
   case $var1 in
       ''|\#*) continue ;;         # skip blank lines and lines starting with #
   esac
   echo "var1: '$var1'"
   # stuff with var1, etc.
done < "${1:-default_config_file}"

while그러면 입력이 명령 목록 대신 루프로 리디렉션되어야 합니다 . 비어 있지 않으면 "${1:-default_config_file}"첫 번째 명령줄 인수로 확장되고, 그렇지 않으면 다음으로 확장됩니다 default_config_file. 기본값 문자열 등에서 변수 확장을 사용할 수도 있습니다.

최소한의 전처리에 관심이 있으므로 이것이 동일하다고 생각하지만 모든 주석도 제거합니다.

while read line; do
    echo "${line%%#*}" | {
        read var1 var2 var3 var4
        [ -z "$var1" ] && continue
        # stuff with var1, etc.
        for i in 1 2 3 4; do eval echo "\"var$i: \$var$i\""; done  #debug only!
    }
done < "${1:-default_config_file}"

이는 쉘 매개변수 확장 하위 문자열 처리 기능을 사용합니다. 제거 후 첫 번째 값과 모든 항목을 제외하고 원래 값으로 확장됩니다 ${line%%#*}. 로드하고 평소대로 계속하십시오. 이제 대신 빈 문자열만 확인하면 되므로 테스트가 단축됩니다.line#var1-4continue#

답변3

임시 파일을 만들지 않고도 이 작업을 수행할 수 있습니다. grep 명령은 빈 줄과 주석 줄을 필터링합니다.

while read var1 var2 var3; do
    echo $var1
    echo $var2
    echo $var3
    echo "etc..."
done < <(grep -v "^#\|^$" /opt/myconfigfile.txt)

관련 정보