다음 항목이 포함된 텍스트 파일을 고려해 보세요.
aaa
bbb
ccc
ddd
eee
fff
ggg
hhh
iii
패턴(예 fff
: )이 주어지면 위 파일을 grep하여 출력을 얻고 싶습니다.
all_lines except (pattern_matching_lines U (B lines_before) U (A lines_after))
예를 들어, B = 2
및 인 경우 A = 1
패턴 =의 출력은 다음 fff
과 같아야 합니다.
aaa
bbb
ccc
hhh
iii
grep이나 기타 명령줄 도구를 사용하여 이 작업을 수행하려면 어떻게 해야 합니까?
내가 시도할 때 주의할 점은 다음과 같습니다.
grep -v 'fff' -A1 -B2 file.txt
나는 내가 원하는 것을 얻지 못했습니다. 대신 나는 다음을 얻습니다.
aaa
bbb
ccc
ddd
eee
fff
--
--
fff
ggg
hhh
iii
답변1
gnu grep
및 를 사용하여 제외하려는 파일 부분을 정확하게 인쇄할 수 있지만 줄 번호를 인쇄하는 스위치를 추가한 -A
다음 출력 형식을 지정하고 명령 스크립트로 전달하여 해당 줄을 제거합니다.-B
-n
sed
grep -n -A1 -B2 PATTERN infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile
grep
이는 다음을 통해 전달된 스키마 파일에도 적용됩니다. -f
예:
grep -n -A1 -B2 -f patterns infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile
3개 이상의 연속된 줄 번호를 범위로 축소하여 예를 들어 ... 대신 사용하면 약간 최적화될 수 있다고 생각 2,6d
하지만 2d;3d;4d;5d;6d
입력에 일치하는 항목이 몇 개만 있으면 수행할 가치가 없습니다.
행 순서를 유지하지 않고 속도가 느려지는 다른 방법은 다음 과 같습니다 comm
.
comm -13 <(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) \
<(nl -ba -nrz -s: infile | sort) | cut -d: -f2-
comm
정렬된 입력이 필요합니다. 즉, 줄 순서는 최종 출력에서 유지되지 않습니다(파일이 이미 정렬되지 않은 경우). 따라서 nl
정렬하기 전에 줄 번호를 매기고 comm -13
고유한 줄만 인쇄하는 데 사용됩니다.두 번째 파일그런 다음 cut
추가된 부분( nl
예: 첫 번째 필드 및 구분 기호 :
) 을 제거합니다
.join
join -t: -j1 -v1 <(nl -ba -nrz -s: infile | sort) \
<(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) | cut -d: -f2-
답변2
대부분의 경우에는 이 작업을 수행하지 않는 것이 더 나을 수 있지만 파일이 다음과 같은 경우에만 가능합니다.진짜sed
크네요. 그렇게 큰 스크립트 파일은 처리 할 수 없습니다 .(이는 약 5000줄 이상의 스크립트에서 발생할 수 있습니다), 일반적인 것은 다음과 같습니다 sed
.
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
이것은 소위 말하는 예입니다.슬라이딩 윈도우입력에. 그것이 작동하는 방식은시야$B
버퍼링 - 인쇄를 시도하기 전에 라인 수를 계산합니다.
실제로 이전 요점을 명확히 해야 할 것 같습니다. 이 솔루션과 다른 솔루션의 주요 성능 제한 요소는 간격과 직접적으로 관련됩니다. 이 솔루션은 더 큰 간격으로 인해 속도가 느려집니다.크기, 시간 간격이 증가하면 속도가 느려집니다.빈도. 즉, 입력 파일이 매우 크더라도 실제 간격 발생이 여전히 매우 드물다면 그의 솔루션이 올바른 선택일 수 있습니다. 그러나 간격 크기가 상대적으로 관리하기 쉽고 자주 발생할 가능성이 있는 경우 이 솔루션을 선택해야 합니다.
워크플로는 다음과 같습니다.
$match
패턴 공간에서 앞의 ewline이 발견 되면\n
그 앞의 각 ewline은 재귀적으로 제거sed
됩니다 .D
\n
- 이전에 패턴 공간을 완전히 비웠
$match
는데, 겹치는 부분을 쉽게 처리하려면 랜드마크를 남겨 두는 것이 더 잘 작동하는 것 같습니다. - 나는 또한
s/.*\n.*\($match\)/\1/
한 번에 그것을 얻고 루프를 피하려고 노력 했지만$A/$B
그것이 클 때D
엘레테 루프가 훨씬 더 빠른 것으로 나타났습니다.
- 이전에 패턴 공간을 완전히 비웠
N
그런 다음 ewline 구분 기호 앞에 있는 추가 입력 줄을 가져오고\n
최근에 사용한 정규식을 참조하여 a를 제거하려고 다시 시도합니다D
./\n.*$match/
//
- 패턴 공간이 일치하면 줄의 시작 부분에서만 이 작업을 수행
$match
할 수 있습니다 . 이전 줄은 모두 지워졌습니다.$match
$B
- 그래서 우리는 사이클을 시작합니다
$A
. - 이 루프를 실행할 때마다 우리는 자신을 패턴 공간의 줄 문자로
s///
바꾸려고 시도하며&
, 성공하면 est는 우리와 전체 백 버퍼를 스크립트에서 완전히 분기하여 다음 입력으로 맨 위에서 다시 시작합니다. 라인(있는 경우).$A
\n
t
$A
t
est가 실패 하면 op 레이블로 돌아가서 다른 입력 줄에서 반복합니다.b
fter를 수집하는 동안 이런 일이 발생하면:t
루프를 다시 시작할 수도 있습니다 .$match
$A
- 그래서 우리는 사이클을 시작합니다
- 함수 루프를 통과하면 마지막 줄(마지막 줄인 경우)을 인쇄
$match
하려고 시도하고 , 그렇지 않은 경우 패턴 공간의 줄 문자로 자신을 바꾸려고 시도합니다 .p
$
!
s///
&
$B
\n
- 또한 이를 테스트하고 성공하면 rint 태그
t
로 이동합니다 .:P
- 그렇지 않은 경우 op로 다시 분기
:t
하고 다른 입력 라인을 버퍼에 추가합니다.
- 또한 이를 테스트하고 성공하면 rint 태그
:P
린트 하도록 두면 패턴 공간에서 첫 번째 줄을P
린트한 후D
제거 하고 나머지 내용과 함께 맨 위에서 스크립트를 다시 실행합니다.\n
그렇다면 이번에는A=2 B=2 match=5; seq 5 | sed...
rint의 첫 번째 반복을 위한 패턴 공간은 :P
다음과 같습니다.
^1\n2\n3$
이것이 sed
프론트 버퍼가 수집되는 $B
방식 입니다. 따라서 출력 카운트 라인 sed
으로 인쇄하십시오.$B
뒤에수집하는 입력입니다. 이는 이전 예를 기반으로 다음 을 인쇄 sed
한다는 것을 의미합니다.P
1
그런 다음 D
이를 삭제하고 다음과 같이 패턴 공간을 스크립트 상단으로 다시 보냅니다.
^2\n3$
...그리고 N
스크립트 상단에서 외부 입력 줄을 검색하므로 다음 반복은 다음과 같습니다.
^2\n3\n4$
따라서 5
입력에서 첫 번째 항목을 찾으면 패턴 공간은 실제로 다음과 같습니다.
^3\n4\n5$
그런 다음 D
선택 루프가 시작되고 완료되면 다음과 같습니다.
^5$
N
외부 입력 라인을 당기면 EOF sed
가 발생하여 종료됩니다. 그 시점에는 P
1행과 2행만 인쇄되었습니다.
실행 예시는 다음과 같습니다.
A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
인쇄:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100
답변3
사용해도 괜찮다면 다음을 사용하세요 vim
.
$ export PAT=fff A=1 B=2
$ vim -Nes "+g/${PAT}/.-${B},.+${A}d" '+w !tee' '+q!' foo
aaa
bbb
ccc
hhh
iii
-Nes
호환되지 않는 자동 Ex 모드를 켭니다. 스크립팅에 유용합니다.+{command}
{command}
vim에게 이 파일을 실행하라고 지시하세요.g/${PAT}/
- 일치하는 모든 줄에/fff/
. 이 방식으로 처리하지 않으려는 정규식 특수 문자가 패턴에 포함되어 있으면 까다로울 수 있습니다..-${B}
- 이 행 위의 1개 행부터 시작.+${A}
- 이 줄 아래로 두 줄 이동합니다(참조:he cmdline-ranges
모두)d
- 행을 삭제합니다.+w !tee
그런 다음 표준 출력에 씁니다.+q!
변경 사항을 저장하지 않고 종료합니다.
변수를 건너뛰고 패턴과 숫자를 직접 사용할 수 있습니다. 나는 그것들을 명시적인 목적으로만 사용합니다.
답변4
임시 파일을 사용하면 충분한 결과를 얻을 수 있습니다.
my_file=file.txt #or =$1 if in a script
#create a file with all the lines to discard, numbered
grep -n -B1 -A5 TBD "$my_file" |cut -d\ -f1|tr -d ':-'|sort > /tmp/___"$my_file"_unpair
#number all the lines
nl -nln "$my_file"|cut -d\ -f1|tr -d ':-'|sort > /tmp/___"$my_file"_all
#join the two, creating a file with the numbers of all the lines to keep
#i.e. of those _not_ found in the "unpair" file
join -v2 /tmp/___"$my_file"_unpair /tmp/___"$my_file"_all|sort -n > /tmp/___"$my_file"_lines_to_keep
#eventually use these line numbers to extract lines from the original file
nl -nln $my_file|join - /tmp/___"$my_file"_lines_to_keep |cut -d\ -f2- > "$my_file"_clean
밝혀지다충분하다프로세스에서 일부 들여쓰기가 손실될 수 있지만 XML 또는 들여쓰기를 구분하지 않는 파일인 경우에는 문제가 되지 않습니다. 이 스크립트는 RAM 드라이브를 사용하기 때문에 이러한 임시 파일을 쓰고 읽는 것은 메모리에서 작업하는 것만큼 빠릅니다.