Sed üzerindeki desen aralığını sil (veya awk) ile sil


28

Desenli bananasatırları ve ondan sonraki 2 satırı kaldıracak aşağıdaki kodu kullanıyorum :

sed '/banana/I,+2 d' file

Çok uzak çok iyi! Fakat daha önce 2 satırı kaldırmak için ihtiyacım var banana, ancak “eksi işareti” veya başka bir şeyle elde edemiyorum (ne grep -v -B2 banana fileyapmalı ama yapmamalı gibi):

teresaejunior@localhost ~ > LC_ALL=C sed '-2,/banana/I d' file
sed: invalid option -- '2'
teresaejunior@localhost ~ > LC_ALL=C sed '/banana/I,-2 d' file
sed: -e expression #1, char 16: unexpected `,'
teresaejunior@localhost ~ > LC_ALL=C sed '/banana/I,2- d' file
sed: -e expression #1, char 17: unknown command: `-'

1
En kolayı, tüm verileri bir diziye yüklemek, istenmeyen satırları atlamak ve geriye kalanları yazdırmaktır awk '{l[m=NR]=$0}/banana/{for(i=NR-2;i<=NR;i++)delete l[i]}END{for(i=1;i<=m;i++)if(i in l)print l[i]}'. Bu verimli değil, bu yüzden sadece bir ipucu, bir çözüm değil.
Manatwork

6
Sadece yap tac file | sed ... | tac. : P
angus

@angus Düşünmedim;)
Teresa e Junior

1
sed '/banana/,+2d' file bunun da işe yarayacağını yapmış olabilirsiniz
Akaks

1
Awk kullanmaya açıksanız, oldukça basit: awk 'tolower($0)~/bandana/{print prev[!idx];print prev[idx]} {idx=!idx;prev[idx]=$0}' filein Bu bir yorum ve bir cevap olmadığı için (zaten başka cevaplar var), çok fazla ayrıntıya girmeyeceğim, ama bunun özü her zaman sizin prev [0] ve prev [1] önceki iki kayıtları, "taze" hangi yineleme üzerinde ama her zaman içinde bağlı prev[idx]yazdırma sırasında, peki yazdırmak !idxardından idxsırayla. Ne olursa olsun, alternatif idxve geçerli kayıt içine yerleştirin prev[idx].
Luv2code

Yanıtlar:


22

Sed geri izlemiyor: bir satır işlendikten sonra biter. Bu nedenle, “bir satır bulun ve önceki N satırlarını yazdırın”, üzerinde çalışmak kolay olmayan “bir satır bulun ve bir sonraki N satırlarını yazdırın” den farklı olarak çalışmaz.

Dosya çok uzun değilse, GNU uzantılarıyla iyi görünüyorsanız tac, dosyanın satırlarını tersine çevirmek için kullanabilirsiniz .

tac | sed '/banana/I,+2 d' | tac

Bir başka saldırı açısı, awk gibi bir araçta kayan bir pencere tutmaktır. Adaptasyon Grep'in -A -B -C anahtarlarına bir alternatif var mı (öncesi ve sonrası birkaç satır basmak için)? (uyarı: minimal olarak test edilmiştir):

#!/bin/sh
{ "exec" "awk" "-f" "$0" "$@"; } # -*-awk-*-
# The array h contains the history of lines that are eligible for being "before" lines.
# The variable skip contains the number of lines to skip.
skip { --skip }
match($0, pattern) { skip = before + after }
NR > before && !skip { print NR h[NR-before] }
{ delete h[NR-before]; h[NR] = $0 }
END { if (!skip) {for (i=NR-before+1; i<=NR; i++) print h[i]} }

Kullanımı: /path/to/script -v pattern='banana' -v before=2


2
sedsürgülü pencereleri de yapabilir, ancak sonuçtaki komut dosyası genellikle kullanımı kolay olacak kadar okunaksızdır awk.
jw013

@Gilles .. awkSenaryo tam olarak doğru değil; boş satırları yazdırır ve son satırları özlüyor. Bu düzeltilmiş gibi görünüyor, ancak ideal veya doğru kendisi olmayabilir: if (NR-before in h) { print...; delete...; }... ve ENDbölümde: for (i in h) print h[i]... Ayrıca, awk betiği eşleşen satırı yazdırıyor, ancak tac/secsürüm yok; ama soru bu konuda biraz belirsiz .. Bir link sağladığınız "orijinal" awk betiği iyi çalışıyor .. Sevdim ... Yukarıdaki "mod" un baskıyı nasıl etkilediğinden emin değilim. satırları ...
Peter.O

@ Peter.O Teşekkürler, awk betiği şimdi daha iyi olmalı. Ve beni 6-8 yıldan daha az sürdü!
Gilles 'SO- kötülük' dur

19

Bu eski veya vim-e ile oldukça kolaydır

    vim -e - $file <<@@@
g/banana/.-2,.d
wq
@@@

İfade şunları okur: Geçerli satır -2'den geçerli satıra kadar uzanan muz içeren her satır için, silin.

Aradaki durum, aralığın geriye ve ileriye doğru aramaları da içerebilmesidir; örneğin, dosyanın elma içeren bir satırla başlayan ve turuncu içeren bir satırla biten ve muzlu bir satır içeren biten:

    vim -e - $file <<@@@
g/banana/?apple?,/orange/d
wq
@@@

7

İçindeki "sürgülü pencereyi" kullanarak perl:

perl -ne 'push @lines, $_;
          splice @lines, 0, 3 if /banana/;
          print shift @lines if @lines > 2
          }{ print @lines;'

6

Bunu oldukça basitçe yapabilirsiniz sed:

printf %s\\n    1 2 3 4match 5match 6 \
                7match 8 9 10 11match |
sed -e'1N;$!N;/\n.*match/!P;D'

Neden birinin başka türlü söyleyeceğini bilmiyorum ama bir satır bulup önceki satırları yazdır sedP için, \ndesen uzayında yalnızca ilk satır sonuna kadar karakter yazan yerleşik kenarlık ilkelini içerir. Tamamlayıcı Dseçkin ilkel, senaryoyu kalanlarla tekrar tekrar geri dönüştürmeden önce aynı desen alanı segmentini kaldırır. Ve onu yuvarlamak için, Neklenen bir \newline karakterinin ardından ext giriş satırını desen uzayına eklemek için bir ilkel var .

Böylece bir satır sed ihtiyacınız şey olmalı. Sadece matchregexp'iniz ne olursa olsun ve altınsınız. Bu da çok hızlı bir çözüm olmalı .

Ayrıca, matchhemen bir başkasının hemen önünde sayılacağını unutmayınmatch önceki iki hat için sessiz çıkışa hem de bir tetikleyici ve sıra baskı sessiz:


1
7match
8
11match

İsteğe bağlı sayıda hat için çalışması için yapmanız gereken tek şey bir ipucu bulmaktır.

Yani:

    printf %s\\n     1 2 3 4 5 6 7match     \
                     8match 9match 10match  \
                     11match 12 13 14 15 16 \
                     17 18 19 20match       |
    sed -e:b -e'$!{N;2,5bb' -e\} -e'/\n.*match/!P;D'

1
11match
12
13
14
20match

... herhangi bir eşleşmeden önceki 5 satırı siler.


1

Kullanarak man 1 ed:

str='
1
2
3
banana
4
5
6
banana
8
9
10
'

# using Bash
cat <<-'EOF' | ed -s <(echo "$str")  | sed -e '1{/^$/d;}' -e '2{/^$/d;}'
H
0i


.
,g/banana/km\
'm-2,'md
,p
q
EOF
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.