이 'find'명령은 어떻게 실행 하나 비 - 바이너리 파일에서만 수행 할 수 있습니까?


8

재귀 디렉터리 계층의 모든 파일에서 후행 공백을 제거하고 싶습니다. 나는 이것을 사용한다 :

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

이 방법은 효과적이지만 발견 된 바이너리 파일에서 후행 "공백"을 제거하므로 바람직하지 않습니다.

어떻게 알 수 있습니까? find 바이너리 파일에서이 명령을 실행하지 않으려면?


유닉스 파일 시스템은 "binary"와 "non-binary"파일을 구별하지 않는다; 파일 내부를 들여다 보지 않고 파일에있는 데이터의 유형을 알 수있는 방법이 없습니다.
Wooble

@ 워블 : 맞습니다.하지만 다음과 같은 명령이 있습니다. file 데이터를 검사 할 수 있습니다.
John Feminella

답변:


4

당신은 유닉스를 사용할 수 있습니다. file 명령을 사용하면 원하지 않는 파일을 식별하는 데 도움이 될 수 있지만 필자가 원하지 않는 파일 대신 히트 할 파일을 명시 적으로 지정하면 더 좋을 것이라고 생각합니다.

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

소스 제어 파일로 이동하는 것을 피하기 위해 다음과 같은 것을 원할 수 있습니다.

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

쉘에 따라 백 슬래시가 필요할 수도 있고 없을 수도 있습니다.


2
나는 당신에 대해 모른다. 그러나 모든 Java 소스 파일은 항상 표준 UTF-8 형식이므로 sed 명령은 항상 그것들 모두로 올바른 일을하지는 않을 것입니다. 나는 또한없는 시스템을 가지고있다. -i ~에 대한 옵션 sed . 이식성있는 쉘 명령을 작성하는 것은 어렵습니다. 그렇지 않습니까?
tchrist

4

명령 행에서 수행 할 수 있습니다.

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

3

가장 간단하고 가장 이식 가능한 대답은 이것을 실행하는 것입니다.

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

내가 왜 아래 명령을 사용 하는지를 보여주는 이유와 ISO-8859-1 (Latin-1)과 UTF-8 같은 trans-ASCII 텍스트 파일을 다루는 방법도 설명합니다. -ASCII 공백


이야기의 나머지 부분

문제는 발견 (1)은 -T filetest 연산자를 사용하거나 UTF-8, 사실상 표준 유니 코드 인코딩을 감지해야하는 경우에는 인코딩을 인식하지 않습니다.

바이너리 파일을 버리는 레이어를 통해 파일 이름 목록을 실행하면됩니다. 예를 들어

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

그러나 이제는 파일 이름에 공백 문자를 사용하는 데 어려움이 있으므로 null 종료를 사용하여 늦게해야합니다.

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

당신이 할 수있는 또 다른 일은 사용하지 않는 것입니다. find 그러나 find2perlPerl이 이해하고 있기 때문에 -T 이미:

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

Perl이 UTF-8 형식의 파일을 사용한다고 가정하려면

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

또는 결과 스크립트를 파일에 저장하고 편집 할 수 있습니다. 당신은 정말로 정말로 -T 이전 파일에 대한 파일 테스트가 아닌 일반 파일 인 경우에만 -f. 그렇지 않으면 장치 스페셜을 열거 나 FIFO를 차단하는 위험이 있습니다.

그러나, 당신이 모든 것을 할 예정이라면, 당신은 건너 뛸지도 모른다. sed (1) 모두. 한 가지로 POSIX 버전의 sed (1) 이해할 수 없다. -i반면 Perl의 모든 버전은 그렇습니다. 다음날 sed 사랑스럽게 매우 유용하게 충당 -i tl이 처음 나타나는 Perl의 옵션.

이것은 또한 정규 표현식을 고칠 수있는 기회를 제공합니다. 실제로 0이 아닌 하나 이상의 후행 가로 공백과 일치하는 패턴을 사용해야합니다. 그렇지 않으면 불필요한 복사로 인해 더 느리게 실행됩니다. 즉, 이것은 :

 s/[ \t]*$//

해야한다

 s/[ \t]+$//

그러나, 어떻게 얻을 sed (1) 비 POSIX 확장을 필요로한다는 것을 이해하기 위해, 보통 -R Solaris 또는 Linux와 같은 System Ⅴ Units의 경우 또는 -E OpenBSD 나 MacOS 같은 BSD 용. 나는 그것이 AIX 하에서는 불가능하다고 생각한다. 휴대용 쉘 스크립트보다 휴대용 쉘을 작성하는 것이 더 쉽습니다.

0xA0에 대한 경고

그것들은 ASCII의 유일한 수평 공백 문자이지만, ISO-8859-1과 결과적으로 Unicode는 코드 포인트 U + 00A0에서 NO BREAK SPACE를 갖습니다. 이것은 많은 유니 코드 코드에서 발견되는 비 ASCII 문자 중 상위 2 개 중 하나입니다. 그리고 나는 사람들의 정규식 코드를 깨뜨린 것을 최근에 보았습니다.

그럼 왜 이렇게하면 안되니?

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

UTF-8 파일을 처리 할 수있는 경우 -CSD, Perl v5.10 이상을 실행중인 경우 다음을 사용할 수 있습니다. \h 수평 공백 및 \R 일반적인 줄 바꿈은 다음을 포함합니다. \r, \n, \r\n, \f, \cK, \x{2028}, 및 \x{2029}:

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

모든 UTF-8 파일에서 줄 바꿈에 관계없이 후행 수평 공백을 제거합니다 (유니 코드 문자 속성 HorizSpace ) 각 줄 끝에 유니 코드 줄 바꿈 (CRLF 콤보 포함) 전에 발생하는 성가신 NO-BREAK SPACE가 포함됩니다.

그것도 훨씬 더 휴대용입니다. sed (1) 버전 만 있기 때문에 버전 (1) 구현,하지만 많은 sed (1).

내가 거기에 남아있는 주요 문제는 발견 (1), 왜냐하면 어떤 진정으로 저항하는 시스템 (당신이 AIX와 솔라리스인지 알기 때문에)은 초 임계적인 -print0 지령. 그것이 당신의 상황이라면, 당신은 단지 File::Find 모듈을 Perl에서 직접 다운로드하고 다른 유닉스 유틸리티를 사용하지 마십시오. 다음은 다른 어떤 것에 의존하지 않는 순수한 Perl 버전의 코드입니다.

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

ASCII 또는 ISO-8859-1 텍스트 파일로 실행 중이면 괜찮습니다. 그러나 ASCII 또는 UTF-8 파일로 실행중인 경우에는 -CSD Perl에 대한 내부 호출의 스위치.

ASCII, ISO-8859-1 및 UTF-8 세 가지 모두를 혼합하여 인코딩 한 경우 다른 문제가 발생할 수 있습니다. : 당신은 파일 단위로 인코딩을 알아 내야 할 것이며, 결코 그것을 추측 할 수있는 좋은 방법은 없습니다.

유니 코드 공백

기록을 위해 유니 코드는 26 가지의 공백 문자를 가지고 있습니다. 당신이 사용할 수있는 그만큼 무협 유용 이것들을 냄새 맡기. 처음 세 개의 가로 공백 문자 만이 거의 보입니다.

$ 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은 파일이 바이너리인지 여부를 식별하는 데 매우 유용합니다. Solaris 이외에는 기본적으로 GNU grep과 함께 제공되지 않는 다른 플랫폼이있을 것이라고 확신하지만 Solaris와 마찬가지로 설치가 가능할 것으로 확신합니다.

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

솔라리스에 있다면, grep/opt/csw/bin/ggrep.

그만큼 grep 플래그는 다음을 수행합니다. l 일치하는 파일의 파일 이름 만 나열하고, R 재귀 적이다. I 텍스트 파일 만 일치 (바이너리 파일 무시) P perl 호환 정규식 구문입니다.

perl 부분은 파일의 위치를 ​​수정하여 모든 후행 공백 / 탭을 삭제합니다.

마지막으로, UTF8이 문제라면, tchrist의 답은 내 것이어야합니다. grep 당신은 UTF8 지원으로 구축되었습니다 (보통 패키지 관리자는 그런 종류의 기능을 제공하려고합니다).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.