수년 동안 이 사이트를 사용해오면서 항상 답변이 있기 때문에 질문을 한 번도 하지 않았습니다(보통 많은 답변). 나는 이것도 마찬가지라고 확신하지만 평생 동안 그것을 찾을 수 없습니다.
임의 길이의 줄이 많은 파일이 포함된 디렉터리가 있습니다.
a.txt
b.txt
c.txt
d.txt
eg.txt
그런 다음 문자열 목록이 포함된 파일이 있습니다.
opq 111
rst 222
uvw 333
xyz 444
각 txt 파일에는 바꾸고 싶은 문자열이 있습니다.
a.txt has a#P#b
b.txt has c#P#d
c.txt has e#P#f
d.txt has g#P#h
#P#
문자열 파일의 두 번째 "열"로 바꾸고 싶습니다 . 이것은 파일당 한 번만 발생합니다 #P#
(제가 파일을 거기에 넣었기 때문입니다). 결과는 다음과 같습니다
a.txt has a111b
b.txt has c222d
c.txt has e333f
d.txt has g444h
"불변" 가정은 내 디렉토리에 있는 줄 eg.txt
수만큼 파일이 있고 .txt
알파벳 순서로 정렬되어 있다는 것입니다. 의 행은 eg.txt
"열" 1을 기준으로 사전순으로 정렬됩니다.
for 루프에서 awk
and sed
(실제로 는)를 사용하여 이 작업을 수행하려고 했지만 sd
"소스"와 "대상"을 한 줄씩 읽을 수는 없습니다.
나는 결과를 얻는 방법에 대해 까다롭지 않습니다. 현재는 많은 라인이나 파일(지금은 15라인과 15개 파일)을 다루고 있지 않지만 가끔 그런 경우가 있습니다. Arch 및 Debian 기반 Linux 배포판(때때로 WSL 2)에서 zsh를 셸로 사용합니다.
답변이 있으시면 사과드립니다. 나는 이 프로젝트를 찾기 위해 지난 이틀 동안 정말 열심히 일해왔지만 지금은 머리가 지쳤습니다.
편집: 디렉터리의 파일에 다양한 길이의 줄이 많이 있고 주어진 문자열이 #P#
파일당 한 번만 나타남을 명확히 하기 위해 업데이트되었습니다.
답변1
GNU awk를 사용한 "내부" 편집 ARGIND
:
awk -i inplace '
NR == FNR { map[NR]=$2 }
NR != FNR { sub(/#P#/,map[ARGIND]) }
1' eg.txt ?.txt
위의 내용은 대체 텍스트에 eg.txt
공백이나 &
s가 포함되어 있지 않다고 가정합니다.
답변2
준비
각 파일에는 한 줄만 있습니다.
$ grep -- . ?.txt
a.txt:a#P#b
b.txt:c#P#d
c.txt:e#P#f
d.txt:g#P#h
$ cat input
opq 111
rst 222
uvw 333
xyz 444
해결책
sed
각 파일에 대해 셸 루프 호출을 수행합니다.
for file in ?.txt; do
read -r dummy new_string rest
sed -- "s/#P#/$new_string/g" "$file"
done <input
a111b
c222d
e333f
g444h
파일 변경 결과에 만족한다면 sed -i
GNU sed
, Compatible, sed -i ''
FreeBSD, Compatible로 변경하세요.sed
위의 내용은 행에 , 또는 문자가 input
포함되어 있지 않다고 가정합니다 . 가능하다면 먼저 백슬래시가 있는 항목을 이스케이프 처리해야 합니다.&
/
\
답변3
#!/bin/sh
mv eg.txt eg.input
awk 'NR==FNR{a[++i]=$2;next}{sub("#P#",a[++j]);print>(FILENAME".new")}' eg.input ./*.txt &&
for f in *.txt; do mv "$f.new" "$f"; done
mv eg.input eg.txt
eg.txt
awk 줄이 수정되어야 하는 파일로만 확장되도록 이름을 변경한 eg.input
다음 다시 되돌립니다 .*.txt
NR==FNR{ #For the first file, eg.input
a[++i]=$2 #Put the second field in the array `a`
next #Skip the rest of the code
}
{ #For the other files
sub("#P#",a[++j]) #Make the substitution
print>(FILENAME".new") #Print to the line to `FILENAME`.new
}
그런 다음 for 루프에서 이전 *.txt
파일 내용을 파일 내용으로 덮어씁니다 *.new
. *.new
파일이 올바른지 확인할 때까지 for 루프를 억제할 수 있습니다 .
일부 awk 구현은 많은 열린 파일을 처리할 수 없습니다(GNU awk는 가능). awk가 "열린 파일이 너무 많습니다"라는 오류로 종료되면 이 변형을 사용하세요.
awk 'NR==FNR{a[++i]=$2;next}FNR==1{close(fn);fn=FILENAME".new"}{sub("#P#",a[++j]);print>fn}'
답변4
eg.txt
opq 111
rst 222
uvw 333
xyz 444
a.txt
a#P#b
12345
apple
b.txt
c#P#d
56788
주문하다
j=1;for i in "a.txt" "b.txt" ; do b=`sed -n ''$j'p' eg.txt| awk '{print $2}'`;sed "s/#P#/$b/g" $i;echo "=================";j=$(($j+1)); done
output
below are the output of a.txt
a111b
12345
apple
=================
below are the output of b.txt
c222d
56788
=================