Làm thế nào để tôi chạy lệnh `find` này, nhưng chỉ trên các tệp không nhị phân?


8

Tôi muốn xóa khoảng trắng theo dõi khỏi tất cả các tệp trong hệ thống phân cấp thư mục đệ quy. Tôi sử dụng cái này:

find * -type f -exec sed 's/[ \t]*$//' -i {} \;

Điều này hoạt động, nhưng cũng sẽ xóa "khoảng trắng" theo dõi khỏi các tệp nhị phân được tìm thấy, điều không mong muốn.

Làm thế nào để tôi biết findđể tránh chạy lệnh này trên các tệp nhị phân?


Các hệ thống tệp Unix không phân biệt giữa các tệp "nhị phân" và "không nhị phân"; không có cách nào để biết loại dữ liệu nào trong tệp mà không nhìn vào bên trong nó.
Wooble

@Wooble: Điều đó đúng, nhưng có những lệnh như filecó thể kiểm tra dữ liệu.
John Femaleella

Câu trả lời:


4

Bạn có thể thử sử dụng filelệnh Unix để giúp xác định các tệp bạn không muốn, nhưng tôi nghĩ có thể tốt hơn nếu bạn chỉ định rõ ràng những tệp bạn muốn nhấn thay vì những tệp bạn không muốn.

find * -type f \( -name \*.java -o -name \*.c -o -name \*.sql \) -exec sed 's/[ \t]*$//' -i {} \;

để tránh truy cập vào các tệp kiểm soát nguồn, bạn có thể muốn một cái gì đó như

find * \! \( -name .svn -prune \) -type f \( -name \*.java -o -name \*.c -o -name \*.sql \) -exec sed 's/[ \t]*$//' -i {} \;

Bạn có thể hoặc không cần một số dấu gạch chéo ngược tùy thuộc vào vỏ của bạn.


2
Tôi không biết về bạn, nhưng tất cả các tệp nguồn Java của chúng tôi luôn ở dạng UTF-8 tiêu chuẩn, vì vậy lệnh sed sẽ không luôn luôn làm đúng với tất cả các tệp đó. Tôi cũng có hệ thống mà không có -itùy chọn để sed . Thật khó để viết một lệnh shell di động, phải không?
tchrist

4

Nó có thể được thực hiện trên dòng lệnh.

$ find . -type f -print|xargs file|grep ASCII|cut -d: -f1|xargs sed 's/[ \t]*$//' -i

3

Câu trả lời đơn giản và dễ mang theo nhất là chạy cái này:

#!/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;

Tôi giải thích lý do tại sao bên dưới, nơi tôi cũng trình bày cách thực hiện bằng cách chỉ sử dụng dòng lệnh, cũng như cách xử lý các tệp văn bản trans-ASCII như ISO-8859-1 (Latin-1) và UTF-8, mà không có -ASCII khoảng trắng trong chúng.


Phần còn lại của câu chuyện

Vấn đề là find (1) không hỗ trợ -Ttoán tử filetest, cũng như không nhận ra mã hóa nếu có - điều mà bạn thực sự cần để phát hiện UTF-8, mã hóa Unicode tiêu chuẩn trên thực tế.

Những gì bạn có thể làm là chạy danh sách tên tệp thông qua một lớp phát ra các tệp nhị phân. Ví dụ

$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'

Tuy nhiên, bây giờ bạn gặp rắc rối với khoảng trắng trong tên tệp của mình, vì vậy bạn cần phải kết thúc điều này bằng cách chấm dứt null:

$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'

Một điều khác bạn có thể làm là sử dụng không , findnhưng find2perlvì Perl -Tđã hiểu :

$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl

Và nếu bạn muốn Perl giả sử các tệp của nó ở dạng UTF-8, hãy sử dụng

$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD

Hoặc bạn có thể lưu tập lệnh kết quả trong một tệp và chỉnh sửa nó. Bạn thực sự không nên chỉ chạy -Tfiletest trên bất kỳ tệp cũ nào, mà chỉ chạy trên những tệp đơn giản như được xác định trước -f. Nếu không, bạn có nguy cơ mở các thiết bị đặc biệt, chặn trên fifos, v.v.

Tuy nhiên, nếu bạn định làm tất cả những điều đó, bạn cũng có thể bỏ qua sed (1) hoàn toàn. Đối với một điều, nó dễ mang theo hơn, vì phiên bản POSIX của sed (1) không hiểu -i, trong khi tất cả các phiên bản của Perl đều có. Các phiên bản Latterday của sed yêu thương chiếm đoạt -itùy chọn rất hữu ích từ Perl nơi ti xuất hiện lần đầu tiên.

Điều này cũng cung cấp cho bạn cơ hội để sửa regex của bạn, quá. Bạn thực sự nên sử dụng một mẫu phù hợp với một hoặc nhiều khoảng trắng ngang, không chỉ bằng 0, hoặc bạn sẽ chạy chậm hơn từ sao chép không cần thiết. Đó là, đây:

 s/[ \t]*$//

nên là

 s/[ \t]+$//

Tuy nhiên, làm thế nào để có được sed (1) để hiểu rằng cần có phần mở rộng không phải POSIX, thường là -Rcho Hệ thống Ⅴ Các thông báo như Solaris hoặc Linux, hoặc -Echo các BSD như OpenBSD hoặc MacOS. Tôi nghi ngờ điều đó là không thể theo AIX. Thật dễ dàng để viết một shell di động hơn là một kịch bản shell di động, bạn biết đấy.

Cảnh báo trên 0xA0

Mặc dù đó là các ký tự khoảng trắng ngang duy nhất trong ASCII, cả ISO-8859-1 và do đó Unicode cũng có KHÔNG GIAN KHÔNG-BREAK tại điểm mã U + 00A0. Đây là một trong hai ký tự không phải ASCII hàng đầu được tìm thấy trong nhiều tập đoàn Unicode và gần đây tôi đã thấy rất nhiều mã regex của mọi người bị phá vỡ vì họ quên mất nó.

Vậy tại sao bạn không làm điều này:

$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'

Nếu bạn có thể có các file UTF-8 để đối phó với, add -CSD, và nếu bạn đang chạy Perl v5.10 hoặc cao hơn, bạn có thể sử dụng \hcho khoảng trắng ngang và \Rcho một linebreak chung chung, trong đó bao gồm \r, \n, \r\n, \f, \cK, \x{2028}, và \x{2029}:

$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -CSD -i -pe 's/\h+(?=\R*$)//'

Điều đó sẽ hoạt động trên tất cả các tệp UTF-8 bất kể ngắt dòng của chúng, loại bỏ khoảng trắng ngang (thuộc tính ký tự Unicode HorizSpace) bao gồm KHÔNG GIAN NO-BREAK xuất hiện trước khi ngắt dòng Unicode (bao gồm cả combo CRLF) ở cuối mỗi dòng.

Nó cũng dễ mang theo hơn nhiều so với phiên bản sed (1), vì chỉ có một triển khai perl (1), nhưng nhiều phiên bản sed (1).

Vấn đề chính tôi thấy còn lại là với find (1), vì trên một số hệ thống tính toán lại thực sự (bạn biết bạn là ai, AIX và Solaris), nó sẽ không hiểu được -print0chỉ thị siêu tới hạn . Nếu đó là tình huống của bạn, thì bạn chỉ nên sử dụng File::Findmô-đun từ Perl trực tiếp và không sử dụng các tiện ích Unix khác. Đây là phiên bản Perl thuần của mã của bạn mà không dựa vào bất cứ thứ gì khác:

#!/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;

Nếu bạn đang chạy trên các tệp văn bản ASCII hoặc ISO-8859-1, điều đó tốt, nhưng nếu bạn đang chạy với các tệp ASCII hoặc UTF-8, hãy thêm -CSDvào các công tắc trong cuộc gọi bên trong tới Perl.

Nếu bạn có mã hóa hỗn hợp của cả ba ASCII, ISO-8859-1 và UTF-8, thì tôi sợ bạn gặp vấn đề khác. :( Bạn sẽ phải tìm ra mã hóa trên cơ sở mỗi tệp và không bao giờ có cách nào tốt để đoán điều đó.

Không gian Unicode

Đối với bản ghi, Unicode có 26 ký tự khoảng trắng khác nhau. Bạn có thể sử dụng các unichars tiện ích để sniff này ra ngoài. Chỉ có ba ký tự khoảng trắng ngang đầu tiên hầu như chưa từng thấy:

$ 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

0

GNU grep khá giỏi trong việc xác định xem một tệp có phải là nhị phân hay không. Khác với Solaris Tôi chắc chắn có các nền tảng khác không được cài đặt GNU grep theo mặc định, nhưng như Solaris tôi chắc chắn bạn có thể cài đặt nó.

perl -pi -e 's{[ \t]+$}{}g' `grep -lRIP '[ \t]+$' .`

Nếu bạn ở Solaris, bạn sẽ thay thế grepbằng /opt/csw/bin/ggrep.

Các grepcờ thực hiện như sau: lchỉ liệt kê tên tệp cho các tệp phù hợp, Rlà đệ quy, Ichỉ khớp với các tệp văn bản (bỏ qua các tệp nhị phân) và Pdành cho cú pháp biểu thức chính quy tương thích perl.

Phần perl sửa đổi tệp tại chỗ, xóa tất cả các dấu cách / tab dấu.

Cuối cùng: nếu UTF8 là một vấn đề, câu trả lời của tchrist kết hợp với của tôi là đủ, miễn là bản dựng của grepbạn đã được xây dựng với sự hỗ trợ UTF8 (mặc dù vậy, thường thì các nhà bảo trì gói cố gắng cung cấp loại chức năng đó).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.