정확히 한 번 문자를 포함하는 경우 줄을 제거하는 방법


10

특정 문자가 포함 된 파일에서 한 줄만 제거하고 싶습니다. 두 번 이상 존재하거나 존재하지 않으면 파일에 줄을 유지하십시오.

예를 들면 다음과 같습니다.

DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC

자, 내가 제거 할 문자가 C있으므로, 명령 라인을 제거해야 FGTHDC하고 JUTDYC그들이 가지고 있기 때문에 C정확히 한 번만.

어떻게 하나 사용하여이 작업을 수행 할 수 있습니다 sed또는 awk?

답변:


20

에서 awk당신이 무엇이든 필드 분리를 설정할 수 있습니다. 로 설정하면의 C발생 횟수만큼 +1 필드가 표시됩니다 C.

따라서 awk -F'C' '{print NF}' <<< "C1C2C3"당신이 얻는 다면 4: CCC3 C초로 구성 되므로 4 개의 필드로 구성됩니다.

C정확히 한 번 발생하는 줄을 제거하려고합니다 . 이것을 고려하여 귀하의 경우 정확히 두 개의 C필드 가있는 줄을 제거하고 싶을 것 입니다. 따라서 그냥 건너 뛰십시오.

$ awk -F'C' 'NF!=2' file
DTHGTY
HYTRHD
HTCCYD

4
awk필드 구분 기호 의 적절한 사용 !
Valentin B.

기본 경우 (FS = "")에서와 같이 삽입하면 선행 공백 ($ 1 = 줄의 첫 번째 공백이 아님)과 반복 (필드 1과 필드 2를 구분하기 위해 5 개의 공백이있을 수 있음)은 무시합니다. 아마 특별하게 취급됩니까? (그것을보기 위해, awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'일부 줄을 여러 줄로 나누고, 다른 줄은 공백으로 시작합니다.)
Olivier Dulac

2
@OlivierDulac, 예, 공간은 POSIX에 지정된대로 특별하게 처리 됩니다 .
와일드 카드

8

sed 접근법 :

sed -i '/^[^C]*C[^C]*$/d' input

-i 옵션으로 전체 파일 수정 가능

/^[^C]*C[^C]*$/- C한 번만 포함 된 줄과 일치

d -일치하는 줄 삭제


8

이것은 다음과 sed같이 수행 할 수 있습니다 :

암호:

sed '/C.*C/p;/C/d' file1

결과 :

DTHGTY
HYTRHD
HTCCYD

어떻게?

  1. 최소 두 개의 C비아 사본으로 모든 줄을 일치시키고 인쇄하십시오./C.*C/p
  2. C비아가 있는 줄을 삭제하십시오. /C/d여기에는 1 단계에서 이미 인쇄 된 줄이 포함됩니다.
  3. 나머지 줄은 기본 인쇄

2
영리한 대체 접근법; 나는 그것을 좋아한다.
와일드 카드

6

이것은 정확히 한 번의 C 발생으로 줄을 제거합니다.

grep -v '^[^C]*C[^C]*$' file

정규식 [^C]은 C (또는 개행 문자)가 아닌 하나의 문자와 일치하며 반복 연산자 (일명 Kleene star) *는 이전 식의 반복을 0 개 이상 지정합니다.

grep(및 대부분의 다른 텍스트 지향 도구) 의 기본 출력은 표준 출력입니다. 새 파일로 리디렉션하고 원하는 경우 원본 파일 위로 이동하십시오. sed -i내부 편집에 동일한 정규식을 사용할 수 있습니다 .

sed -i '/^[^C]*C[^C]*$/d' file

(일부 플랫폼, 특히 macOS를 포함한 * BSD의 경우 -i옵션 에는와 같은 인수가 필요합니다 -i ''.)


1
sed -i '/^[^C]*C[^C]*$/d' file-이전에 게시 된 것처럼 들립니다. 표절은 어떻게 생각하십니까?
RomanPerekhrest

1
실제로 일부 중복이 있습니다. 나는 grep대답으로 시작 했지만 분명히 sed -i변형으로 쉽게 확장됩니다 . 이전 grep답변 을 찾고 있었기 때문에 귀하의 답변을 보지 못했습니다 .
tripleee

1
그냥 분명히 피하기 위해 더 안전 -ised대신 새 파일로 리디렉션하고 경우에 그와 함께 원본을 대체 sed유틸리티가 오류없이 종료.
Kusalananda

2
또는grep -vx '[^C]*C[^C]*'
Stéphane Chazelas

@Kusalananda 그러나 grep더 명확하고 강력 하기 때문에 특히 유용 할 수 있습니다 (특히 sed덜 유익한 종료 코드가 있음).
tripleee

4

수정 된 내용을 표준 출력으로 인쇄하지 않고 파일의 스크립트 편집을위한 POSIX 도구는 ex입니다.

printf '%s\n' 'g/^[^C]*C[^C]*$/d' x | ex file.txt

물론 Sed 버전이 지원 하는sed -i 경우 사용할있습니다 . 다른 유형의 시스템에서 실행되는 스크립트를 작성하는 경우 이식성이 없다는 점에 유의하십시오.


David Foerster는 다음과 같이 논평했습니다.

왜 사용 printf하고 echo있거나 아닌지에 대한 이유가 ex -c COMMAND있습니까?

답 : 예.

들어 printfecho그것은 이동성의 문제입니다; 에코보다 printf가 더 나은 이유를 참조하십시오 . 또한를 사용하여 명령 사이에 개행을 산재하는 것이 더 쉽습니다 printf.

들어 printf ... | exex -c ..., 그것은 오류 처리의 문제입니다. 이 특정 명령의 경우 중요하지 않지만 일반적으로 중요합니다. 예를 들어

ex -c '%s/this pattern is not in the file/replacement text/g | x' filename

스크립트에서. 다음과 대조하십시오 :

printf '%s\n' '%s/no matching lines/replacement/g' x | ex file

첫 번째는 중단되고 입력을 기다립니다. 두 번째는 ex명령 이 EOF를 수신하면 종료 되므로 스크립트가 계속됩니다. 와 같은 대체 해결 방법이 s///e있지만 POSIX에서 지정하지 않았습니다. 위에 표시된 휴대용 양식을 사용하는 것이 좋습니다.

를 들어 g명령이 있어야 끝에 줄 바꿈, 그리고 내가 사용하는 것을 선호 printf명령을 포장하는 대신 작은 따옴표로 줄 바꿈을 삽입.


1
왜 사용 printf하고 echo있거나 아닌지에 대한 이유가 ex -c COMMAND있습니까?
David Foerster 17

@DavidFoerster입니다. 댓글로 답하기 시작했지만 시간이 오래 걸리므로 답변에 추가했습니다.
와일드 카드

감사합니다 +1! 나는 printfvs. 에 대해 알았지 만 echo(일반적으로 echo인수가 하드 코딩 될 때 선호 하지만) ex지금까지 광범위하게 사용하지는 않았습니다 .
David Foerster

2

다음은 perl을 사용하는 몇 가지 옵션입니다.

단일 문자 만 일치하므로 tr/C//(대체없이 번역)을 사용 하여 다음과 일치하는 수를 반환 할 수 있습니다 C.

perl -lne 'print if tr/C// != 1' file

보다 일반적으로 다중 문자 문자열 또는 정규식을 일치 시키려면 다음을 사용할 수 있습니다.

perl -lne 'print if (@m = /C/g) != 1' file

이것은 정규 표현식의 일치를 /C/g리스트에 할당하고 리스트 @m의 길이가 아닌 경우 행을 인쇄합니다 1.

-i스위치는 "현재 위치에서"편집에 추가 할 수 있습니다.


2
sed -e '
  s/C/&/2;t   # when 2nd C matches skip processing and print
  /C/d        # either one C or no C, so delete on C
'

sed -e '
   /C/!b     # no C, skip processing and print
   /C.*C/!d  # not(at least 2 C) => 1 C => delete
'

perl -lne 's/C/C/g == 1 or print'

참고는 GNU를 가정합니다 sed, t #...일반적이라는 라벨로 분기 것이 #...다른 대부분의 sed구현.
Stéphane Chazelas

심지어는 !b분기 라벨이나 뒤에 줄 바꿈을 제외하고 아무것도처럼하지 않기 때문에 GNU가 나오지입니다.

예, b, t, :, }(그리고 r file, w file...) 같은 줄에 후 명령을 가질 수 없습니다. 별도의 -e옵션을 사용할 수도 있습니다.
Stéphane Chazelas

펄 옵션이 올바른 출력을 생성하지 않습니다. g수정자를 추가하는 것을 잊었다 고 생각합니다 .
Tom Fenech

@TomFenech 맞습니다. 나는 그것을 고치고있다. 감사.

1

awk구체적으로 원하는 사람 은

awk '/C[^C]*C/{next}//{print}'

패턴과 일치하면 줄을 건너 뛰고 그렇지 않으면 인쇄하십시오. 실제로 필요하지는 않습니다. 기본 인쇄를 {print}사용할 수 //있지만 철자가 더 명확하다고 생각합니다.

내 첫 번째 생각은 egrep -v같은 패턴 으로 사용 하는 것이었지만 실제로 제기 된 질문에 대답하지는 않습니다.


1
무슨 일이 있어도 요점은 무엇입니까 {next}? 그냥 말하면 awk '/pattern/ {next} 1'패턴과 일치하지 않는 모든 줄이 인쇄됩니다. 또는 awk '!/pattern/'직접 인쇄하는 것이 좋습니다 .
fedorqui

@fedorqui 좋은 점 !/pattern/(어쨌든 내 마음을 미끄러 뜨 렸지만 ) //{print}은 cryptic보다 자기 설명 이 훨씬 낫습니다 1. 코드를 유지 관리하기 위해 다음 사람의 능력과 유창함이 가장 낮다고 가정하십시오.
nigel222
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.