Dizeyi sıralı dizinle değiştir


10

Birisi bunu başarmak için zarif bir yol önerebilir mi?

Giriş:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

çıktı:

test      instant1  ()

test      instant2  ()

test      instant1000()

Boş satırlar girdi dosyalarımda ve aynı dizinde aynı anda işlemem gereken birçok dosya var.

Ben aynı dir birçok dosyaları değiştirmek için çalıştı ve işe yaramadı.

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

hatalar:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

ve ben de denedim:

perl -i -pe 's/instant/$& . ++$n/ge' *.vs

İşe yaradı, ancak dizin birinden diğerine artmaya devam etti. Yeni bir dosyaya geçtikten sonra bunu 1'e sıfırlamak istiyorum. İyi bir öneriniz var mı?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

çalışır ancak değiştirilen tüm diğer dosyalar değiştirilmemelidir. Dosyaları sadece ile değiştirmeyi tercih ediyorum *.txt.


Ve hepsi sadece boş satırlardan test instant ()mı oluşuyor ?
terdon

Çift aralıklı satırları geri koydum, genellikle bu sitenin işaretlemesini nasıl kullanacağını bilmeyen yeni kullanıcıların bir işareti, bu yüzden terdon dosya içeriği bloğunuzu düzgün bir şekilde girintili hale getirirken dosya içeriği olarak gösterir. Umarım şimdi tamam.
Timo

Yanıtlar:


14
perl -pe 's/instant/$& . ++$n/ge'

veya GNU ile awk:

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

Dosyaları yerinde düzenlemek için şu -iseçeneği ekleyin perl:

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*.vs

Veya özyineli olarak:

find . -name '*.vs' -type f -exec perl -pi -e '
  s/instant/$& . ++$n{$ARGV}/ge' {} +

açıklamalar

perl -pe 's/instant/$& . ++$n/ge'

-pgirişi satır satır işlemek, -eher satır için iletilen ifadeyi değerlendirmek ve yazdırmaktır. Her satır için ( s/re/repl/flagsoperatörü kullanarak ) instantkendisi ( $&) ve bir değişkenin artan değeri yerine geçeriz ++$n. gBayrak global (sadece bir kez) değişiklik olduğunu ve ebu nedenle yedek perl kodlarına olarak yorumlanır olduğu e valuate (bir sabit dize).

Bir perl çağrısının birden fazla dosyayı işlediği yerinde düzenleme $niçin, her dosyada sıfırlamak istiyoruz . Bunun yerine, $n{$ARGV}( $ARGVşu anda işlenen dosya nerede ) kullanıyoruz.

Bu awkbiraz açıklamayı hak ediyor.

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

GNU'nun awkkeyfi dizelerdeki kayıtları (hatta regexps) ayırmak için kullanıyoruz . İle -vRS=instant, r̲ecord hızlandırıcıyı olarak ayarladık instant. boş dize olacağı son kayıt haricinde , tipik RTolarak eşleşenle eşleşen değişkendir . Yukarıdaki girişte kayıtlar ( ) ve kayıt sonlandırıcılar ( ): ( ):RSinstant$0RT[$0|RT]

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

Bu yüzden tek yapmamız gereken, ilk kayıt hariç her kaydın başına bir artan sayı girmek.

Yukarıda yaptığımız şey budur. İlk kayıt niçin boş olacak. ORS'i ( çıkış r̲ecord s )eparatorunu ) RT'ye ayarladık , böylece awk yazdırın n $0 RT. Bunu ++nher zaman true (sıfır olmayan bir sayı) olarak değerlendiren bir koşul olan ikinci ifade ( ) üzerine yapar ve bu nedenle $0 ORSher kayıt için varsayılan baskı (baskı ) gerçekleştirilir.



5

sediş için gerçekten en iyi araç değil, daha iyi komut dosyası oluşturma özelliklerine sahip bir şey istiyorsunuz. İşte bazı seçenekler:

  • perl

    perl -00pe 's/instant/$& . $./e' file 

    -pVasıta ile verilir ne olursa olsun komut uygulandıktan sonra "her satırını yazdırmak" -e. -00Kayıtları (çizgiler) bu yüzden "paragraf modu" konulu dönüşler ardışık yeni satır tanımlanmıştır ( \n) karakterleri, bu doğru çift aralıklı çizgilerle başa sağlar. $&, eşleşen son kalıptır ve $.giriş dosyasının geçerli satır numarasıdır. eİçinde s///ebeni ikame operatörü ifadeleri değerlendirmek için izin verir.

  • awk (bu, verilerinizin boşlukla ayrılmış üç alanla tam olarak gösterildiği gibi olduğunu varsayar)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    Burada, kdeğişkeni kyalnızca geçerli satır boş değilse artırırız /./, bu durumda gerekli bilgileri de yazdırırız. Boş satırlar olduğu gibi yazdırılır.

  • çeşitli kabuklar

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 

    Burada, her girdi satırı boşluk alanına otomatik olarak bölünür ve alanlar $a, $bve olarak kaydedilir $c. Daha sonra, döngü içinde, boş olmayan $cher satır için bir tane artırılır $ave geçerli değeri ikinci alanın yanında yazdırılır $b,.

NOT: yukarıdaki çözümlerin tümü , dosyadaki tüm satırların aynı biçimde olduğunu varsayar . Değilse, @ Stephane'nin cevabı gidilecek yoldur.


Birçok dosyayla uğraşmak ve bunu geçerli dizindeki tüm dosyalara yapmak istediğinizi varsayarsak , bunu kullanabilirsiniz:

for file in ./*; do perl -i -00pe 's/instant/$& . $./e' "$file"; done

DİKKAT: Gerek, daha karmaşık bir şeyle uğraşmak için gitmek (varsayarak için Yani, eğer boşluk olmadan basit dosya adları varsayar ksh93, zshya bash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -00pe 's/instant/$& . $./e' "$file"
done

perl betiği çalışır. ancak çizgiler çift boşluksa küçük bir sorun vardır.
user3342338

@ user3342338 evet, geçerli satır numarasını kullandığımdan beri sayacı artıracak. Stephane'nin daha sağlam olduğunu söylediğim gibi bu çok naif bir yaklaşım. Boş satırlarınız varsa veya satırlarınızdan herhangi biri gösterdiklerinizden farklıysa bunların hiçbiri işe yaramaz.
terdon

@ user3342338 güncellenmiş cevaba bakın. Şimdi hepsi çift aralıklı dosyalar için çalışmalıdır.
terdon

Büyük cevap ve alternatif yöntemler seçeneği !! Teşekkürler
Madivad

0

Bu sorunu çözmek sedistiyorsanız, şöyle bir şey kullanabilirsiniz bash:

i=0
while read -r line; do
  sed "s/\(instant\)/\1${i}/" <<< "${line}"
  [[ ${line} =~ instant ]] && i=$(( i + 1 ))
done < file

ya da daha taşınabilir bir çözüm:

i=0
while read -r line; do
  echo "${line}" | sed "s/\(instant\)/\1${i}/"
  if echo "${line}" | grep -q inst; then
    i=$(( i + 1 ))
  fi
done < file
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.