Sondaki yeni satırı bash'da nasıl silebilirim?


10

Perl'ler gibi davranan bir şey arıyorum chomp. Ben sadece yeni bir satır ise, son karakteri eksi girişini yazdıran bir komut arıyorum:

$ printf "one\ntwo\n" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done
$ printf "one\ntwo" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done

(Bash ve Zsh'daki komut ikamesi, tüm yeni satırları siler, ancak en fazla bir yeni satır silen bir şey arıyorum.)

Yanıtlar:


9

Bu çalışmalı:

printf "one\ntwo\n" | awk 'NR>1{print PREV} {PREV=$0} END{printf("%s",$0)}' ; echo " done"

Komut dosyası her zaman geçerli yerine önceki satırı yazdırır ve son satıra farklı davranılır.

Daha ayrıntılı olarak ne yapar:

  1. NR>1{print PREV} Önceki satırı yazdır (ilk kez hariç).
  2. {PREV=$0}Geçerli satırı PREVdeğişkene kaydeder.
  3. END{printf("%s",$0)} Son olarak, son satırı satır sonuyla yazdırın.

Ayrıca, bunun sonunda en fazla bir boş satırı kaldıracağını unutmayın (kaldırma desteği yoktur "one\ntwo\n\n\n").


15

Şunlar perlolmadan kullanabilirsiniz chomp:

$ printf "one\ntwo\n" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

$ printf "one\ntwo" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

Ama neden chompkendini kullanmıyorsun :

$ printf "one\ntwo\n" | perl -pe 'chomp if eof'; echo " done"

4

Kesin bir eşdeğer istiyorsanız chomp, aklıma gelen ilk yöntem , LatinSuD'un zaten yayınladığı awk çözümüdür . Uygulanmayan, chompancak chompsıklıkla kullanılan bazı ortak görevleri uygulayan başka yöntemler ekleyeceğim .

Bir değişkene metin doldurduğunuzda, sondaki tüm yeni satırlar çıkarılır. Yani tüm bu komutlar aynı tek satırlık çıktıyı üretir:

echo "$(printf 'one\ntwo') done"
echo "$(printf 'one\ntwo\n') done"
echo "$(printf 'one\ntwo\n\n') done"
echo "$(printf 'one\ntwo\n\n\n\n\n\n\n\n\n\n') done"

Bir dosyanın veya komutun çıktısının son satırına biraz metin eklemek istiyorsanız, seduygun olabilir. GNU sed ve diğer birçok modern uygulamada, girdi yeni bir satırda sona ermese bile bu işe yarar¹; ancak, henüz yoksa bir satırsonu eklemez.

sed '$ s/$/ done/'

¹ Ancak bu tüm sed uygulamaları ile çalışmaz: sed bir metin işleme aracıdır ve boş olmayan ve yeni satır karakteri ile bitmeyen bir dosya bir metin dosyası değildir.


Bu, tam olarak eşdeğer değil chompolarak, chompsadece en çok bir arka yeni satır ile siler.
Flimm

@Flimm Evet, en belirgin tam karşılığı chomp, LatinSuD'un zaten yayınladığı awk çözümü olacaktır. Ancak çoğu durumda chompsadece bir iş yapmak için bir araçtır ve bazı ortak görevleri yerine getirmenin yollarını sağlarım. Bunu açıklığa kavuşturmak için cevabımı güncelleyeyim.
Gilles 'SO- kötü olmayı bırak

1

Başka bir perlyaklaşım. Bu, belleğe tüm girdiyi okur, bu nedenle büyük miktarda veri için iyi bir fikir olmayabilir (cuonglm veya awkyaklaşımını kullanın ):

$ printf "one\ntwo\n" | perl -0777pe 's/\n$//'; echo " done"
one
two done

Teşekkürler, @ StéphaneChazelas, düzeltildi. Nedense, bu anahtar beni her zaman karıştırıyor !
terdon

0

Bunu bir yerlerde bir github deposundan aldım, ama nerede bulamıyorum

silme-sondaki boş hatları-sed

#!/bin/bash
#
# Delete all trailing blank lines.
# From http://sed.sourceforge.net/sed1line.txt
#
# Version: 1.3.0
# Created: 2011-01-02
# Updated: 2015-01-25
# Contact: Joel Parker Henderson (joel@joelparkerhenderson.com)
# License: GPL
##
set -euf
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'

0

Öz

Satırları satırsonu olmadan yazdırın, yalnızca yazdırılacak başka satır varsa satırsonu ekleyin.

$ printf 'one\ntwo\n' | 

     awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }';   echo " done"

one
two done

Diğer çözümler

Bir dosyayla çalışsaydık, sadece bir karakteri ondan kesebiliriz (eğer bir satırsonu ile bitiyorsa):

removeTrailNewline () {[[$ (kuyruk -c 1 "$ 1")]] || || kes -s-1 "$ 1"; }

Bu, dosyadan yalnızca bir karakteri okuması ve ardından truncatetüm dosyayı okumadan doğrudan ( ) kaldırması gerektiğinden hızlı bir çözümdür .

Ancak, stdin (akış) verileriyle çalışırken, verilerin tümü okunmalıdır. Ve okunduğu anda "tüketilir". Geri iz yok (kesik gibi). Bir akışın sonunu bulmak için akışın sonuna kadar okumamız gerekir. Bu noktada, giriş akışında geri gitmenin bir yolu yoktur, veriler zaten "tüketilmiştir". Bu, biz akışın sonuna kadar ve sonra arabellekteki verilerle bir şeyler yapana kadar verilerin bir çeşit tamponda saklanması gerektiği anlamına gelir .

Çözümlerin en belirgin olanı, akışı bir dosyaya dönüştürmek ve o dosyayı işlemektir. Ancak soru akışın bir tür filtresini istiyor. Ek dosyaların kullanımı ile ilgili değildir.

değişken

Saf çözüm, tüm girdiyi bir değişkene yakalamak olacaktır:

FilterOne(){ filecontents=$(cat; echo "x");        # capture the whole input
             filecontents=${filecontents%x};       # Remove the "x" added above.
             nl=$'\n';                             # use a variable for newline.
             printf '%s' "${filecontents%"$nl"}";  # Remove newline (if it exists).
       }

printf 'one\ntwo'     | FilterOne ; echo 1done
printf 'one\ntwo\n'   | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done

hafıza

Sed ile tüm bir dosyayı belleğe yüklemek mümkündür. Sed'de son satırdaki son satırdan kaçınmak mümkün değildir. GNU sed sondaki satırsonu yazdırmaktan kaçınabilir, ancak yalnızca kaynak dosya zaten eksikse. Yani, hayır, basit sed yardım edemez.

-zSeçeneği ile GNU awk hariç :

sed -z 's/\(.*\)\n$/\1/'

Awk (herhangi bir awk) ile, tüm akışı ve printfsondaki yeni satır olmadan kaydırın.

awk '    { content = content $0 RS } 
     END { gsub( "\n$", "", content ); printf( "%s", content ) }
    '

Bir dosyanın tamamını belleğe yüklemek iyi bir fikir olmayabilir, çok fazla bellek tüketebilir.

Bellekte iki satır

Awk olarak, önceki satırı bir değişkende saklayıp mevcut olanı yazdırarak döngü başına iki satır işleyebiliriz:

awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'

Doğrudan işleme

Ama daha iyisini yapabiliriz.

Biz bir satır olmadan mevcut çizgisini yazdırmak ve bir sonraki çizgi var olan tek bir yeni satır yazdırmak, biz bir zaman ve gelecek son satırında bir satır işlemek değil bir eğik yeni satır vardır:

awk 'NR == 1 {printf ("% s", $ 0); sonraki}; {printf ("\ n% s", $ 0)} '

Veya başka bir şekilde yazılmış:

awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'

Veya:

awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'

Yani:

$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
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.