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은 Test One" 및 "101은 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개의 프로세스로. 이는 목록에 행이 많지만 example.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는 아닙니다.

관련 정보