abstrak
Cetak baris tanpa baris baru, tambahkan baris baru hanya jika ada baris lain untuk dicetak.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Solusi lain
Jika kami bekerja dengan file, kami dapat memotong satu karakter saja dari file tersebut (jika berakhir pada baris baru):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || truncate -s-1 "$ 1"; }
Itu adalah solusi cepat karena hanya perlu membaca satu karakter dari file dan kemudian menghapusnya langsung ( truncate
) tanpa membaca seluruh file.
Namun, saat bekerja dengan data dari stdin (aliran) data harus dibaca, semuanya. Dan, "dikonsumsi" segera setelah dibaca. Tidak ada mundur (seperti dengan terpotong). Untuk menemukan akhir suatu aliran, kita perlu membaca sampai ke ujung aliran. Pada saat itu, tidak ada cara untuk kembali pada input stream, data telah "dikonsumsi". Ini berarti bahwa data harus disimpan dalam beberapa bentuk buffer sampai kami mencocokkan akhir aliran dan kemudian melakukan sesuatu dengan data dalam buffer.
Solusi yang paling jelas adalah mengubah aliran menjadi file dan memproses file itu. Tetapi pertanyaannya meminta semacam filter aliran. Bukan tentang penggunaan file tambahan.
variabel
Solusi naif adalah dengan menangkap seluruh input ke dalam variabel:
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
Penyimpanan
Dimungkinkan untuk memuat seluruh file dalam memori dengan sed. Selain itu, tidak mungkin untuk menghindari baris baru yang tertinggal di baris terakhir. GNU sed mungkin menghindari pencetakan baris tambahan, tetapi hanya jika file sumber sudah hilang. Jadi, tidak, sed sederhana tidak bisa membantu.
Kecuali pada GNU awk dengan -z
opsi:
sed -z 's/\(.*\)\n$/\1/'
Dengan awk (sembarang awk), hirup seluruh aliran, dan printf
tanpa baris baru.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Memuat seluruh file ke dalam memori mungkin bukan ide yang baik, mungkin menghabiskan banyak memori.
Dua baris dalam memori
Dalam awk, kita dapat memproses dua baris per loop dengan menyimpan baris sebelumnya dalam sebuah variabel dan mencetak yang sekarang:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Pemrosesan langsung
Tapi kita bisa melakukan yang lebih baik.
Jika kami mencetak baris saat ini tanpa baris baru dan mencetak baris baru hanya ketika baris berikutnya ada, kami memproses satu baris sekaligus dan baris terakhir tidak akan memiliki baris tambahan:
awk 'NR == 1 {printf ("% s", $ 0); selanjutnya}; {printf ("\ n% s", $ 0)} '
Atau, ditulis dengan cara lain:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Atau:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Begitu:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, karenachomp
hanya menghapus paling banyak satu trailing newline.