내 스크립트에 문제가 있습니다.
전주곡 첫째, 다음과 같은 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는 아닙니다.