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