![grep -v: 일치하는 항목의 첫 번째(또는 마지막) N 줄만 제외하는 방법은 무엇입니까?](https://linux55.com/image/102450/grep%20-v%3A%20%EC%9D%BC%EC%B9%98%ED%95%98%EB%8A%94%20%ED%95%AD%EB%AA%A9%EC%9D%98%20%EC%B2%AB%20%EB%B2%88%EC%A7%B8(%EB%98%90%EB%8A%94%20%EB%A7%88%EC%A7%80%EB%A7%89)%20N%20%EC%A4%84%EB%A7%8C%20%EC%A0%9C%EC%99%B8%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95%EC%9D%80%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
때로는 표 형식 데이터에 매우 짜증나는 행이 있는 경우가 있습니다.
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에 대한 나의 제한된 지식 때문일 수 있습니다).