Jawaban paling sederhana dan paling portabel adalah menjalankan ini:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
my @dirs = (@ARGV == 0) ? <*> : @ARGV;
find sub {
next unless -f && -T;
system('perl', '-i', '-pe', 's/[\t\xA0 ]+$//', $File::Find::name);
} => @dirs;
Saya menjelaskan mengapa di bawah ini, di mana saya juga menunjukkan bagaimana melakukannya hanya dengan menggunakan baris perintah, serta bagaimana menangani file teks trans-ASCII seperti ISO-8859-1 (Latin-1) dan UTF-8, yang sebelumnya tidak memiliki Ruang putih-ASCII di dalamnya.
Sisa dari Kisah
Masalahnya adalah bahwa find (1) tidak mendukung -T
operator filetest, juga tidak mengenali pengkodean jika itu - yang Anda benar-benar perlu mendeteksi UTF-8, pengkodean Unicode standar de facto standar.
Yang bisa Anda lakukan adalah menjalankan daftar nama file melalui lapisan yang membuang file biner. Sebagai contoh
$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
Namun sekarang Anda memiliki masalah dengan spasi putih di nama file Anda, jadi Anda harus mengakhiri ini dengan penghentian nol:
$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
Hal lain yang bisa Anda lakukan adalah menggunakan find
tetapi find2perl
, karena Perl -T
sudah mengerti :
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl
Dan jika Anda ingin Perl menganggap file-nya berada di UTF-8, gunakan
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD
Atau Anda dapat menyimpan skrip yang dihasilkan dalam file dan mengeditnya. Anda benar-benar benar-benar tidak boleh hanya menjalankan -T
filetest pada file lama apa pun, tetapi hanya pada mereka yang file biasa seperti yang pertama kali ditentukan oleh -f
. Kalau tidak, Anda berisiko membuka spesial perangkat, memblokir fifos, dll.
Namun, jika Anda akan melakukan semua itu, Anda mungkin melewatkan sed (1) sama sekali. Untuk satu hal, ini lebih portabel, karena versi POSIX sed (1) tidak mengerti -i
, sedangkan semua versi Perl lakukan. Versi terakhir dari sed dengan penuh kasih sayang menggunakan opsi yang sangat berguna -i
dari Perl tempat saya pertama kali muncul.
Ini juga memberi Anda kesempatan untuk memperbaiki regex Anda juga. Anda harus benar-benar menggunakan pola yang cocok dengan satu atau lebih spasi spasi horizontal, tidak hanya nol, atau Anda akan berjalan lebih lambat dari penyalinan yang tidak perlu. Yaitu, ini:
s/[ \t]*$//
seharusnya
s/[ \t]+$//
Namun, bagaimana untuk sed (1) untuk memahami yang membutuhkan ekstensi-POSIX non, biasanya baik -R
untuk Sistem Ⅴ beragam Unix seperti Solaris atau Linux, atau -E
untuk yang BSD seperti OpenBSD atau MacOS. Saya menduga itu tidak mungkin di bawah AIX. Sayangnya, lebih mudah menulis shell portabel daripada skrip shell portabel.
Peringatan pada 0xA0
Walaupun itu adalah satu-satunya karakter spasi putih horizontal di ASCII, ISO-8859-1 dan juga Unicode memiliki ruang NO-BREAK pada titik kode U + 00A0. Ini adalah salah satu dari dua karakter non-ASCII teratas yang ditemukan di banyak Unicode corpora, dan akhir-akhir ini saya melihat banyak kode regex orang rusak karena mereka lupa.
Jadi kenapa tidak Anda lakukan saja ini:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'
Jika Anda mungkin memiliki UTF-8 file untuk menangani, add -CSD
, dan jika Anda menjalankan Perl v5.10 atau lebih, Anda dapat menggunakan \h
untuk spasi horizontal dan \R
untuk linebreak generik, yang meliputi \r
, \n
, \r\n
, \f
, \cK
, \x{2028}
, dan \x{2029}
:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -CSD -i -pe 's/\h+(?=\R*$)//'
Itu akan bekerja pada semua file UTF-8 tidak peduli linebreak mereka, menghilangkan spasi spasi horizontal (properti karakter Unicode HorizSpace
) termasuk spasi NO-BREAK SPEAK yang terjadi sebelum linebreak Unicode (termasuk CRLF combo) di akhir setiap baris.
Ini juga jauh lebih portabel daripada versi sed (1), karena hanya ada satu perl (1) implementasi, tetapi banyak sed (1).
Masalah utama yang saya lihat masih ada di sana adalah dengan find (1), karena pada beberapa sistem yang benar-benar bandel (Anda tahu siapa Anda, AIX dan Solaris), ia tidak akan memahami -print0
arahan superkritis . Jika itu situasi Anda, maka Anda harus menggunakan File::Find
modul dari Perl secara langsung, dan tidak menggunakan utilitas Unix lainnya. Ini adalah versi Perl murni dari kode Anda yang tidak bergantung pada hal lain:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
my @dirs = (@ARGV == 0) ? <*> : @ARGV;
find sub {
next unless -f && -T;
system('perl', '-i', '-pe', 's/[\t\xA0 ]+$//', $File::Find::name);
} => @dirs;
Jika Anda menjalankan hanya pada file teks ASCII atau ISO-8859-1, itu bagus, tetapi jika Anda menjalankan dengan file ASCII atau UTF-8, tambahkan -CSD
ke switch di panggilan interior ke Perl.
Jika Anda memiliki penyandian campuran dari ketiga ASCII, ISO-8859-1, dan UTF-8, maka saya khawatir Anda memiliki masalah lain. :( Anda harus mencari tahu penyandian berdasarkan per-file, dan tidak pernah ada cara yang baik untuk menebaknya.
Ruang Putih Unicode
Sebagai catatan, Unicode memiliki 26 karakter spasi yang berbeda. Anda dapat menggunakan yang unichars utilitas untuk mengendus keluar ini. Hanya tiga karakter spasi horisontal pertama yang hampir pernah terlihat:
$ unichars '\h'
---- U+0009 CHARACTER TABULATION
---- U+0020 SPACE
---- U+00A0 NO-BREAK SPACE
---- U+1680 OGHAM SPACE MARK
---- U+180E MONGOLIAN VOWEL SEPARATOR
---- U+2000 EN QUAD
---- U+2001 EM QUAD
---- U+2002 EN SPACE
---- U+2003 EM SPACE
---- U+2004 THREE-PER-EM SPACE
---- U+2005 FOUR-PER-EM SPACE
---- U+2006 SIX-PER-EM SPACE
---- U+2007 FIGURE SPACE
---- U+2008 PUNCTUATION SPACE
---- U+2009 THIN SPACE
---- U+200A HAIR SPACE
---- U+202F NARROW NO-BREAK SPACE
---- U+205F MEDIUM MATHEMATICAL SPACE
---- U+3000 IDEOGRAPHIC SPACE
$ unichars '\v'
---- U+000A LINE FEED (LF)
---- U+000B LINE TABULATION
---- U+000C FORM FEED (FF)
---- U+000D CARRIAGE RETURN (CR)
---- U+0085 NEXT LINE (NEL)
---- U+2028 LINE SEPARATOR
---- U+2029 PARAGRAPH SEPARATOR