awk가 "공백"을 구분 기호로 무시하지 않는 이유는 무엇입니까?

awk가 "공백"을 구분 기호로 무시하지 않는 이유는 무엇입니까?

내 스크립트에 문제가 있습니다.

전주곡 첫째, 다음과 같은 100줄 파일 목록이 있습니다.

100;TEST ONE
101;TEST TWO
...
200;TEST HUNDRED

각 줄에는 2개의 인수가 있습니다. 예를 들어 첫 번째 줄의 인수는 "645", "TEST ONE"입니다. 따라서 세미콜론은 구분 기호입니다.

두 인수를 두 변수에 모두 넣어야 합니다. $id와 $name이 될 것이라고 가정해 보겠습니다. 각 줄마다 $id 및 $name 값이 다릅니다. 예를 들어, 두 번째 줄 $id = "646" 및 $name = "TEST TWO"입니다.

그런 다음 샘플 파일을 가져와 사전 정의된 키워드를 $id 및 $name 값으로 변경해야 합니다. 샘플 파일은 다음과 같습니다.

xxx is yyy

결과적으로 저는 다양한 콘텐츠가 포함된 100개의 파일을 갖고 싶습니다. 각 파일에는 모든 줄의 $id 및 $name 데이터가 포함되어야 합니다. 그리고 $name 값으로 이름을 지정해야 합니다.

내 스크립트가 있습니다.

#!/bin/bash -x
rm -f output/*

for i in $(cat list)
    do

        id="$(printf "$i" | awk -F ';' '{print $1}')"
        name="$(printf "$i" | awk -F ';' '{print $2}')"

        cp sample.xml output/input.tmp

        sed -i -e "s/xxx/$id/g" output/input.tmp
        sed -i -e "s/yyy/$name/g" output/input.tmp

        mv output/input.tmp output/$name.xml


    done

그래서 저는 목록 파일을 한 줄씩 읽으려고 노력합니다. 모든 줄마다 두 개의 변수를 얻은 다음 이를 사용하여 샘플 파일의 키워드(xxx 및 yyy)를 바꾼 다음 결과를 저장합니다.

하지만 문제가 발생했습니다.

결과적으로 출력 파일은 1개뿐입니다. 그리고 디버그 상태가 좋지 않은 것 같습니다.

내 목록 파일에 단 2줄만 있는 디버그 창은 다음과 같습니다. 출력 파일이 하나만 있습니다. 파일 이름은 "TEST"이고 "101 is TEST"라는 문자열을 포함합니다.

두 개의 파일이 예상됩니다: "TEST ONE", "TEST TWO" 그리고 "100 is TEST ONE" 및 "101 is TEST TWO"를 포함해야 합니다.

디버그 스크린샷

보시다시피 두 번째 변수에는 공백이 있습니다(예: "TEST ONE"). 문제가 공간 특수 기호와 관련된 것 같은데 이유를 모르겠습니다. ";"에 -F awk 매개변수를 넣었으므로 awk는 세미콜론만 구분자로 해석해야 합니다!

내가 뭘 잘못했나요?

답변1

내가 올바르게 이해했다면 while 루프와 변수 확장을 사용할 수 있습니다

while IFS= read -r line; do 
  id="${line%;*}"
  name="${line#*;}"
  cp sample.xml output/input.tmp
  sed -i -e "s/xxx/$id/g" output/input.tmp
  sed -i -e "s/yyy/$name/g" output/input.tmp
  mv output/input.tmp output/"$name".xml
done < file

@steeldriver가 제안한대로, 여기에 (더 우아한) 옵션이 있습니다:

while IFS=';' read -r id name; do 
  cp sample.xml output/input.tmp
  sed -i -e "s/xxx/$id/g" output/input.tmp
  sed -i -e "s/yyy/$name/g" output/input.tmp
  mv output/input.tmp output/"$name".xml
done < file

답변2

인용 !!. 이 줄의 인용문이 누락되었습니다.

mv output/input.tmp output/$name.xml

그것은해야한다:

mv output/input.tmp output/"$name".xml

공백이 포함된 파일 이름 문제를 방지합니다.

그리고 확장은 $(cat list)쉘에 의해 분할(및 덩어리)되고 있으며, 이는 공백에서도 분리됩니다.

아마도 다음 스크립트로 변경할 수 있습니다.

#!/bin/bash -x
rm -f output/*

inputfile=output/input.tmp

while read -r line
do
    id=${line%%;*}
    name=${line##*;}

    cp sample.xml "$inputfile"
    sed -i -e "s/xxx/$id/g" "$inputfile"
    sed -i -e "s/yyy/$name/g" "$inputfile"
    mv "$inputfile"  output/"$name".xml; echo

done <list

답변3

awk가 예상한 결과를 생성하지 못하는 이유는 파일을 반복하는 방식 때문입니다. 을 사용하여 반복하면 for i in $(cat file)줄이 아닌 단어(IFS로 분할)를 반복하는 것입니다. 파일을 한 줄씩 읽으려면 다음을 사용하십시오 while read.

while read -r line; do
    ...
done < file

자세한 내용은 다음 bash FAQ를 참조하세요.파일(데이터 스트림, 변수)을 한 줄씩(및/또는 필드별로) 읽으려면 어떻게 해야 합니까?

답변4

대안적인 접근법으로서,이 작업은 awk로 할 수 있습니다각 라인마다 4개가 아닌 1개의 프로세스로 처리됩니다. 목록에 행이 많지만 Sample.xml이 작은 경우 이 방법이 가장 유용할 수 있습니다.

awk -F';' 'FNR==NR{x=x $0 RS; next} 
{t=x; gsub(/xxx/,$1,t); gsub(/yyy/,$2,t); f="output/"$2".xml"; printf "%s",t >f; close(f)}
' sample.xml list
# shown with unnecessary linebreaks for clarity, but you can put it all on one line

목록에 Q에 설명된 대로 CRLF 줄 끝(일명 DOS 또는 Windows 형식)이 있고 이를 먼저 제거할 수 없거나 (쉽게) 제거하고 싶지 않은 경우 awk가 이를 처리할 수도 있습니다. 두 번째 {삽입 직후 sub(/\r$/,"",$0);(또는 $2원하는 경우).

Perl도 이 작업을 수행할 수 있지만(perl은 awk가 할 수 있는 거의 모든 작업을 수행할 수 있음) 좀 더 장황하고 Perl이 일반적으로 사용 가능하지만 awk만큼 POSIX는 아닙니다.

관련 정보