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


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
그냥 분명히 피하기 위해 더 안전 -i에 sed대신 새 파일로 리디렉션하고 경우에 그와 함께 원본을 대체 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있습니까?

답 : 예.

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

들어 printf ... | ex대 ex -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.