grep -v: 일치하는 항목의 첫 번째(또는 마지막) N 줄만 제외하는 방법은 무엇입니까?

grep -v: 일치하는 항목의 첫 번째(또는 마지막) N 줄만 제외하는 방법은 무엇입니까?

때로는 표 형식 데이터에 매우 짜증나는 행이 있는 경우가 있습니다.

column name | other column name
-------------------------------

나는 일반적으로 존재해서는 안 되는 정크 행을 제거하기 위해 합리적으로 고유한 문자열을 전달하는 것을 선호 grep -v하지만, 이 접근 방식의 문제점은 합리적으로 고유한 문자열이 실수로 데이터에 나타나는 경우 심각한 문제라는 것입니다.

삭제할 수 있는 행 수 grep -v(예: 1)를 제한하는 방법이 있습니까? 보너스 포인트의 경우 를 사용하지 않고 끝부터 줄 수를 계산하는 방법이 있습니까 <some command> | tac | grep -v <some stuff> | tac?

답변1

awk첫 번째 것을 무시할 수 있습니다N일치하는 줄(예를 들어, 파일에서 첫 번째와 두 번째 항목만 제거하려는 경우):

n=2
awk -v c=$n '/PATTERN/ && i++ < c {next};1' infile

마지막 것은 무시하세요N일치하는 줄:

awk -v c=${lasttoprint} '!(/PATTERN/ && NR > c)' infile

${lasttoprint}파일에서 마지막으로 일치하는 th+1 줄 번호는 어디에 있습니까 ? n이 줄 번호를 얻는 방법에는 여러 가지가 있습니다. (예를 들어 sed/etc를 통해 일치하는 각 줄 번호를 인쇄한 다음 awk추출 tail | head합니다.)... 한 가지 방법은 다음과 같습니다 gnu awk.

n=2
lasttoprint=$(gawk -v c=$((n+1)) '/PATTERN/{x[NR]};
END{asorti(x,z,"@ind_num_desc");{print z[c]}}' infile)

답변2

sed더 간단한 방법이 제공됩니다.

... |  sed '/some stuff/ {N; s/^.*\n//; :p; N; $q; bp}' | ...

이렇게 하면 첫 번째 항목을 삭제할 수 있습니다.

더 원하는 경우:

sed '1 {h; s/.*/iiii/; x}; /some stuff/ {x; s/^i//; x; td; b; :d; d}'

, 여기서 count of는 i발생 횟수입니다(0이 아닌 1개 이상).

여러 줄의 설명

sed '1 {
    # Save first line in hold buffer, put `i`s to main buffer, swap buffers
    h
    s/^.*$/iiii/
    x
}

# For regexp what we finding
/some stuff/ {
    # Remove one `i` from hold buffer
    x
    s/i//
    x
    # If successful, there was `i`. Jump to `:d`, delete line
    td
    # If not, process next line (print others).
    b
    :d
    d
}'

또한

아마도 이 변형은 나머지 줄을 모두 읽고 한 번에 인쇄하므로 더 빠르게 작동할 것입니다.

sed '1 {h; s/.*/ii/; x}; /a/ {x; s/i//; x; td; :print_all; N; $q; bprint_all; :d; d}'

결과

이 코드를 셸 .bashrc(또는 다른 셸인 경우 셸의 구성)에 넣을 수 있습니다.

dtrash() {
    if [ $# -eq 0 ]
    then
        cat
    elif [ $# -eq 1 ]
    then
        sed "/$1/ {N; s/^.*\n//; :p; N; \$q; bp}"
    else
        count=""
        for i in $(seq $1)
        do
            count="${count}i"
        done
        sed "1 {h; s/.*/$count/; x}; /$2/ {x; s/i//; x; td; :print_all; N; \$q; bprint_all; :d; d}"

    fi
}

다음과 같이 사용하십시오.

# Remove first occurrence
cat file | dtrash 'stuff' 
# Remove four occurrences
cat file | dtrash 4 'stuff'
# Don't modify
cat file | dtrash

답변3

아마도 더 정확한 grep 명령을 사용하면 데이터를 필터링할 가능성이 줄어들 수 있습니다. 예를 들어:

grep -v -F -x 'str1'

다음 줄의 경우완전히str1. 아니면:

grep -v '^str1.*str2$'

"str1"로 시작하고 "str2"로 끝나는 줄의 경우.

답변4

또 다른 가능한 해결책은 bash의 자체 유틸리티를 사용하는 것입니다.

count=1
found=0
cat execute-commons-fileupload.sh | while read line
do 
   if [[ $line == *"myPattern"* ]]
   then 
      if [ $found -eq $count ]
      then 
         echo "$line"
      else 
         found=$(($found+1))
      fi
   else 
     echo "$line"
   fi
done

횟수를 설정하면 제거하려는 패턴의 발생 횟수를 변경할 수 있습니다.

개인적으로 이것은 명령문에 다른 조건을 쉽게 추가할 수 있기 때문에 확장하기가 더 쉬운 것 같습니다 if(그러나 이는 sed에 대한 나의 제한된 지식 때문일 수 있습니다).

관련 정보