여러 파일의 문자열을 한 파일의 각 문자열 줄로 바꿉니다.

여러 파일의 문자열을 한 파일의 각 문자열 줄로 바꿉니다.

수년 동안 이 사이트를 사용해오면서 항상 답변이 있기 때문에 질문을 한 번도 하지 않았습니다(보통 많은 답변). 나는 이것도 마찬가지라고 확신하지만 평생 동안 그것을 찾을 수 없습니다.

임의 길이의 줄이 많은 파일이 포함된 디렉터리가 있습니다.

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 루프에서 awkand 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 -iGNU 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.txtawk 줄이 수정되어야 하는 파일로만 확장되도록 이름을 변경한 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
=================

관련 정보