2016-04-07 17 views
0

Verilen bir kelimeyi tam olarak k kez içeren bir dosyadan tüm satırları aramak zorundayım. Bence grep/sed/awk kullanmalıyım ama nasıl olduğunu bilmiyorum. Benim fikrim böyle sed ve grep kullanarak çizgi ile her satırını kontrol etmek oldu: syntax error near unexpected token 'sed':Belirli bir kelimeyi içeren tüm satırları tam olarak k kez yazdırarak yazdırma

line=1 
while [ (sed -n -'($line)p' $name) -n ]; do 
    if [ (sed -n -'($line)p' $name | grep -w -c $word) -eq "$number" ]; then 
     sed -n -'($line)p' $name 
    fi 
    let line+=1 
done 

Benim ilk sorun aşağıdaki hatayı alıyorum olmasıdır. Daha sonra test dosyamda sed -n -'p1' test.txt | grep -w -c "ab" komutunun dosyamdan ilk satırdaki "ab" görüntülerinin tam sayısını döndürmediğini anlıyorum (1 değerini döndürür ama 3 görüntü var). Benim test.txt dosyası:

abc ab cds ab abcd edfs ab 
kkmd ab jnabc bad ab 
abcdefghijklmnop ab cdab ab ab 
abcde bad abc cdef a b 
+0

"sed" veya "awk" işlevlerini kullandığınızda, dosyanın üzerine dönmenize gerek yoktur. Bu dillerin zaten içsel olarak yaptıkları şey bu. – pfnuesel

+0

@pfnuesel biliyorum ama hat üzerinden hat kontrol etmenin başka bir yolunu bilmiyordum – Papanash

+0

Hattın satırını kontrol ederek, 'sed' yapan şey bu. – pfnuesel

cevap

1

awk! \< ve \> kelime sınırları belirli gawk olabileceğini

$ awk -F'\\<ab\\>' -v count=2 'NF==count+1' file 

kkmd ab jnabc bad ab 

not. Değişken atama için

, ben en kolay

$ word=ab; awk -F"\\\<$word\\\>" -v count=2 'NF==count+1' file 

kkmd ab jnabc bad ab 
+0

benim kabuk komut dosyası komutunuzla tanıtmak ve satır – Papanash

+0

Bu benim için de çalıştığını yazdırmıyor. – pfnuesel

+0

@karakfa nasıl ben takabilmek bir '$ kelime' değişkenle ab? – Papanash

0

Sen grep

grep -E "(${word}.*){${number}}" test.txt 

Bu satıra ${word} ait ${number} oluşumlarını aramaktadır ile yapabilirsiniz. .* numaralı joker karaktere ihtiyaç vardır çünkü biz de ${word} eşleşmelerinin yan yana olmadıkları olayları eşleştirmek istiyoruz.

İşte böyle yapar: kurtarmaya

$ echo 'abc ab cds ab abcd edfs ab 
kkmd ab jnabc bad ab 
abcdefghijklmnop ab cdab ab ab 
abcde bad abc cdef a b' > test.txt 

$ word=abc 
$ number=2 

$ grep -E "(${word}.*){${number}}" test.txt 
> abc ab cds ab abcd edfs ab 
> abcde bad abc cdef a b 
+0

"grep -E" ($ {word}. *) {$ {Number}) "./$ adı" ile denedim ve işe yaramıyor. – Papanash

+0

'$ name' nedir? İstediğiniz dosyayı 'grep' olarak eklemelisiniz. Güncellenmiş cevabımı görün. Dosyanızın adı "$" ise, neyin çalışmadığı konusunda daha açık olabilir misiniz? – pfnuesel

+0

$ adı dosya adıdır, bu herhangi bir metin dosyası için çalışmalıdır. Ve çalışmadığını söylediğimde, çizgileri basmadığını söylemek istedim. – Papanash

1

Sen grep kullanabilirsiniz olacağını düşünüyorum, fakat iki kere kullanmak gerekir. (Tek bir grep kullanamazsınız, çünkü ERE'nin bir dizeyi iptal etme yolu yoktur, yalnızca tek karakterlerle eşleşecek bir parantez ifadesini iptal edebilirsiniz.)

Aşağıdakiler GNU grep v2.5.1 ile test edilmiştir. Eğer (muhtemelen olmayan taşınabilir) kelimesi sınırlayıcı olarak \< ve \> kullanabilirsiniz:

$ word="ab" 
$ < input.txt egrep "(\<$word\>.*){3}" | egrep -v "(\<$word\>.*){4}" 
abc ab cds ab abcd edfs ab 
abcdefghijklmnop ab cdab ab ab 
$ < input.txt egrep "(\<$word\>.*){2}" | egrep -v "(\<$word\>.*){3}" 
kkmd ab jnabc bad ab 
buradaki fikir o zaman, kelimenin N oluşumları ile giriş dosyası hatlarından ayıklamak ve bunun sonucunda gelen şerit olacak olmasıdır

herhangi N + 1 oluşumu olan çizgiler. Tabii ki N'den daha az olan hatlar, ilk grep tarafından eşleştirilmeyecek.


Veya, biraz mazoşist hissediyorsanız ayrıca, saf bash bu yapabilir:

#!/usr/bin/env bash 

# Salt to taste 
word="ab"; num=3 

# Pull content into an array. This isn't strictly necessary, but I like 
# getting my file IO over with quickly if possible. 
readarray lines < input.txt 

# Walk through the array (or you could just walk through the input file) 
for this in "${lines[@]}"; do 

    # Initialize this line's counter array 
    declare -A words=() 

    # Break up the words into array elements 
    x=($this) 

    # Step though the array, counting each unique word 
    for y in "${x[@]}"; do 
    ((words[$y]++)) 
    done 

    # Check the count for "our" word 
    [ "0${words[$word]}" -eq $num ] && echo "$this" 

done 

:

$ word="ab"; num=3 
$ readarray lines < input.txt 
$ for this in "${lines[@]}"; do declare -A words=(); x=($this); for y in "${x[@]}"; do ((words[$y]++)); done; [ "0${words[$word]}" -eq "$num" ] && echo "$this"; done 
abc ab cds ab abcd edfs ab 

abcdefghijklmnop ab cdab ab ab 

kolay okunması (ya komut dizisi) dışarı Broken Bu eğlenceli değil miydi? :)


Ama bu awk seçenek benim için en mantıklı. GNU awk'e bağlı olmayan taşınabilir bir tek liner (yani OS X, BSD, vb.)

Bu "ilginç" kelimesi için sayım num olarak belirtilen ne ise o çizgiyi baskı, her satırda kelimeleri saymak için bir ilişkisel dizi inşa ederek çalışır

. Yukarıdaki bash betiği ile aynı temel kavram, ama awk bunu daha iyi yapmamızı sağlıyor.

+0

Açıklama olmadan downvotes almayı seviyorum. – ghoti

+0

Neden olduğunu bilmiyorum ama ilk örnek için 'abcdefghijklmnop ab cdab ab ab' yazıyor. Aynı soru burada, değişkenlerle çalışmak için nasıl değiştirebilirim? Burada cevaplar için ortak bir tema gibi görünüyor – Papanash

+0

@ghoti - -1 şu anda üç cevapları, tüm ... gamamen2 @ –

0

Belki de sed'u kullanmanız gerekir. Eğer karakter dizileri arıyorsanız, bu şekilde kod kullanabilirsiniz. Bununla birlikte, sözcük kendi başına ve başka bir sözcüğe gömülü sözcük arasında ayrım yapmaz (bu nedenle ab ve abc her ikisini de ab içerir). , Hiçbir şey yazdırılır Varsayılan olarak

word="ab" 
number=2 

sed -n -e "/\($word.*\)\{$(($number + 1))\}/d" -e "/\($word.*\)\{$number\}/p" test.txt 
  • (-n).
  • İlk -e ifadesi, $word'un 3 (veya daha fazla) örneğini arar ve bunları içeren satırları siler (ve sonraki giriş satırına atlar). $(($number + 1)), shell arithmetic.
  • İkinci -e ifadeleri, $word'un 2 yinelenmesini arar (daha fazla olmayacaktır) ve eşleşen satırları yazdırır.

Eğer kendi başına kelimeler istiyorsanız, o zaman çok daha fazla çalışmak zorundasınız. BSD'de (Mac OS X) -E seçeneği ile veya GNU sed ile -r ile tetiklenen uzatılmış düzenli ifadelere gereksiniminiz olacaktır.

number=2 
plus1=$(($number + 1)) 
word=ab 
sed -En -e "/(^|[^[:alnum:]])($word([^[:alnum:]]).*){$plus1}/d" \ 
     -e "/(^|[^[:alnum:]])($word([^[:alnum:]]).*){$number}$word$/d" \ 
     -e "/(^|[^[:alnum:]])($word([^[:alnum:]]|$).*){$number}/p" test.txt 

Bu, önceki sürümle benzerdir, ancak daha hassas sözcük işlemeye sahiptir.

  • (^|[^[:alnum:]]) hattının başlangıç ​​veya bir alfanümerik olmayan karakterin ya arar birimi (Kibritleri durdurmak basamak istemiyorsanız boyunca alpha için alnum değiştirin).
  • İlk -e, satırın veya alfasayısal olmayan bir karakterin başlangıcını arar, ardından sözcük ve alfanümerik olmayan ve sıfır veya daha fazla başka karakter, N + 1 kez arar ve bu satırları siler (sonraki satıra atlar) giriş).
  • İkinci -e, satır veya alfasayısal olmayan bir karakterin başlangıcını arar, ardından sözcük ve bir alfanümerik olmayan ve sıfır veya daha fazla başka karakter N sonra, ve sonra tekrar satırın sonu ve ardından silmeyi arar. çizgiler.
  • Üçüncü -e, satır ya da alfasayısal olmayan bir karakterin başlangıcını arar, ardından sözcük ve alfasayısal olmayan ve sıfır ya da daha fazla başka karakter N sonra bu satırları yazdırır.

(genişletilmiş) giriş dosyası Verilen:

abc NO ab cds ab abcd edfs ab 
kkmd YES ab jnabc bad ab 
abcd NO efghijklmnop ab cdab ab ab 
abcd NO efghijklmnop ab cdab ab ab 
abcd NO e bad abc cdef a b 
ab YES abcd abcd ab 
best YES ab ab candidly 
best YES ab ab candidly 
ab NO abcd abcd ab ab 
hope NO abcd abcd ab ab ab 
nope NO abcd abcd ab ab ab 
ab YES abcd abcd ab not bad 
said YES ab not so bad ab or bad 

Örnek çıkışı:

kkmd YES ab jnabc bad ab 
ab YES abcd abcd ab 
best YES ab ab candidly 
best YES ab ab candidly 
ab YES abcd abcd ab not bad 
said YES ab not so bad ab or bad 

O sed yılında önemsiz bir çaba değildir. Kelime-sınır saptamasına güvenebilecekseniz daha basit olurdu.Örneğin, Perl:

number=2 
plus1=$(($number + 1)) 
word=ab 
perl -n -e "next if /(\b$word\b.*?){$plus1}/; 
      print if /(\b$word\b.*?){$number}/" test.txt 

Bu sed komut aynı çıkışı üretir, fakat \b kelimesinin sınır tespitinin çok daha kolay olduğu (.*? olmayan hırslı uygun bir işlem için çok önemli değildir senaryo).