grep
특정 줄을 전달 keyword
하고 출력을 기존 파일의 특정 줄 번호로 리디렉션하고 싶습니다 .
주문하다
grep "key" temp_file >> desired.txt
내가 필요한 것은 파일의 grepped
특정 줄 번호 에 줄을 추가할 수 있다는 것입니다.x
desired.txt
답변1
수행하는 단계를 올바르게 구성하면 쉬울 수 있습니다. 가장 중요한 것은 과로해도 충돌하지 않는 소스 파일의 버퍼를 얻는 것입니다. 유일한 실제 방법은 다른 파일을 사용하는 것입니다. 쉘을 사용하면 이 작업이 매우 쉽습니다.
{ head -n "$((num_lines_before_insert))"
grep key temp_file; sed \$d
} <<SOURCE_FILE >desired.txt
$( cat <source_file;echo .)
SOURCE_FILE
따라서 대부분의 껍질의 경우( bash
및 는 포함되지만 또는 는 zsh
포함되지 않음 )dash
yash
<<
here_document를 얻으면 쉘은 에 고유한 이름의 임시 파일을 생성하여 지정한 입력 파일 설명자에 배치합니다 ${TMPDIR:-/tmp}
.exec
(또는 기본적으로 0만)- 그리고 즉시 삭제하세요. 명령에 대한 입력으로 사용되는 경우이름이 없는파일 - 파일 시스템에 대한 링크가 남아 있지 않으며 완전히 사라지기 전에 커널이 정리할 때까지 기다립니다. 이것은 올바른 파일입니다. 데이터가 디스크 어딘가에 존재합니다.(또는 가능하다면 적어도 VFS 내에서 tmpfs
)그리고 커널은 적어도 파일 설명자를 해제할 때까지 계속해서 작업을 수행합니다.
이렇게 하면 쉘이 heredoc에 대한 실제 백업 파일을 얻는 한 임시 파일 요구 사항을 처리하는 매우 안전하고 간단한 방법을 나타냅니다. 왜냐하면 해당 파일이 완전히 작성되고 읽기 전에 모든 파일 시스템 이름이 알려지기 때문입니다. 그것에서. 따라서 작업하는 동안 해당 데이터가 변조될 수 없습니다.
위의 블록은 먼저 -를 사용하여 임시 파일에 쓰고 cat
명령 대체에서 모든/후행 공백 줄을 유지합니다 echo
. 이는 파일 끝에 줄을 추가합니다. {
복합 명령문 에서 }
3개 명령의 출력 desired.txt
(그 중 2개는 소스 파일의 꼬리에서 읽음 head
)과 일치하는 항목을 grep
삽입하는 명령이 기록됩니다 key
.
이것이 필요한지 잘 모르겠습니다. 하지만 이와 같은 시퀀스를 사용하여 소스 파일을 간단하고 안전하게 완전히 덮어쓸 수 있다는 것을 보여주는 것과 관련이 있다고 생각합니다.
당신의 껍질이라면아니요heredocs에서 실제 파일을 가져오면 그 기능을 시뮬레이션할 수 있습니다...
{ set "$$" "${TMPDIR:-/tmp}" "$@"
exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head "-n$((before))"
grep ... keyfile; cat
} <source_file 1<>source_file
...이렇게 하면 되돌릴 수 없는 작업을 수행하기 전에 모든 파일이 쓰기 가능하고 파일 설명자에 안전하게 할당되었는지 확인하고 모든 파일 시스템도 정리됩니다.앞으로같은 일을하십시오.
이를 증명하기 위해 제가 실행한 테스트는 다음과 같습니다.
cd /tmp
set "$$" "${TMPDIR:-/tmp}" "$@"
seq 5000000 >test
printf line\ %s\\n 1 2 3 4 5 >test2
{ exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head -n2500000
grep 3 test2;cat
} <test 1<>test
처음 두 개의 파일이 생성되었습니다. 하나는 /tmp/test
500만 개의 행 번호 로 명명되었고 seq
두 번째는 /tmp/test2
5행으로 명명되었습니다. 예를 들어...
line 1
line 2
line 3
line 4
line 5
다음으로 위의 블록을 실행한 다음...
sed -n '1p;$p;2499999,2500002l' <test
wc -l test
...흥미롭게도 이 작업은 실제로 삽입 작업과 동일한 시간이 소요되며 다음과 같이 인쇄됩니다.
1
2499999$
2500000$
line 3$
2500001$
5000000
5000001 test
작동 방식은 다음과 같습니다.
- 리디렉션
1<>
은 중요합니다. 표준 출력에 O_RDWR 플래그를 설정하고 파일에 쓰는 각 프로세스가 파일의 이전 내용을 덮어쓰도록 보장합니다. 즉, 소스/대상 파일이 어느 시점에서든 잘리지 않고 처음부터 끝까지 다시 작성된다는 의미입니다. - 명령 대체를 통해
exec
활성화된 부분을 최대한 빨리 완료 할 수 있습니다.(또는 내가 할 수 있다는 것을 알고 나면). 명령 내에서노클로보음set
, 대화형 셸에서는"${TMPDIR:-/tmp}/$$"
확장된 결과가 이미 존재하는 경우 전체 프로세스가 즉시 중지됩니다exec <"${TMPDIR:-/tmp}/"
. 또는 스크립트에서는 셸이exec
디렉터리를 stdin으로 가져올 수 없기 때문에 의미 있는 오류와 함께 스크립트가 종료되도록 합니다. - 명령 내에서 하위 복사본은 아직 존재하지 않는 임시 파일에
cat
복사되고 이름은 표준 출력에 기록됩니다.source_file
echo
- 모든 파일 핸들이 새 임시 파일로
exec
편집 되면rm
unlink()
현재 남아 있는 유일한 임시 명령문은<
방금 할당된 리디렉션입니다. head
250만 행을 찾아source_file
처음 250만 행을 작성합니다. 요점은 두 파일 모두에서 동일한 오프셋을 찾는 것입니다.- 새로 생성된 tmp 파일이 tmpfs에 있고 소스 파일이 디스크에 있는 경우 I/O의 이 부분이 더 효율적일 수 있다는 점을 명심하십시오(여기에서 I/O가 반전되어
head
디스크 파일에서 읽고 여기에 쓰는 경우). ) 높은 RAM에 있는 파일. - 이 작업을 수행하려면
exec <>"$(... head ... <&1 >&0
tmp 파일을 읽기/쓰기 가능하게 만들고 가능하면head
끝에 줄 수를 /지정하는 데 사용해야 합니다.tail
이 경우 숫자가 정확할 필요도 없습니다.반지비슷한 방식으로 과도 입력 - 한 번에 조금씩 오프셋을 전진시킵니다. 쉘의 내장 함수를read
사용하여 EOF를 테스트하거나wc
루프를 여는 데 사용할 수 있습니다. - 이는 EOF가 표시되지 않기 때문에 표준 입력
cat
에 걸릴 수 있기 때문입니다 .<>
- 새로 생성된 tmp 파일이 tmpfs에 있고 소스 파일이 디스크에 있는 경우 I/O의 이 부분이 더 효율적일 수 있다는 점을 명심하십시오(여기에서 I/O가 반전되어
grep
다른 파일에서 일부 데이터를 읽고 쓰면source_file
다른 곳에서 읽은 것과 동일한 바이트 수만 덮어쓰게 됩니다.cat
grep
stdin의 나머지 부분을 stdout에 기록함으로써 발생할 수 있는 차이점을 수정하십시오1<>source_file
.
답변2
대용량 파일에는 적합하지 않지만 명령 출력을 읽고 주소가 지정된 줄 뒤에 삽입 ed
할 수 있습니다 r
. 예:
ed -s desired.txt <<IN
4r !grep "key" temp_file
w
q
IN
또는 한 줄:
printf '%s\n' '4r !grep "key" temp_file' w q | ed -s desired.txt
다른 줄 번호에 다른 명령의 출력을 삽입할 수 있습니다.거꾸로 일해야 해ed
줄 번호 주소를 반복하는 경우:
ed -s desired.txt <<IN
48r !grep "another_key" another_temp_file
4r !grep "key" temp_file
w
q
IN
답변3
매우 큰 파일 인 경우 desired.txt
최적화가 필요할 수 있습니다. 아마도 쉘 수준에서 더 우아하게 수행될 수 있지만 익숙하지 않기 때문에 tcsh
거기에서 작동하는 것을 제안하고 싶습니다.
sed -n '1,4p' desired.txt >file.tmp
grep "key" temp_file >>file.tmp
sed -n '5,$p' desired.txt >>file.tmp
mv file.tmp desired.txt
답변4
왜 모두가 간단한 일로 문제를 일으키려고 하는지 이해가 안 가나요?
sed -i "4a$(grep "key" temp_file)" desired.txt
4
( 필요한 줄 번호로 변경 )
또는 (여러 줄 grep
출력의 경우)
grep "key" temp_file > grepped.tmp
sed -i '4r grepped.tmp' desired.txt