Shell script Trim 관련 질문드립니다.

nagari의 이미지

안녕하세요. shell script와 관련된 질문드립니다.

Shell script로 바이너리 파일( 혹은 실행 파일 ) 끝을 잘라내는( trim )
방법이 있는지 궁금합니다?

스크립트가 요구하는 기능은 다음과 같습니다.

바이너리 파일 끝부분을 검사하여 끝부분 데이터가 0xff일 때 이 데이터를
파일에서 제거하는 기능.

예)
... abcd efgh ffff ffff ffff [EOF] ==> ... abcd efgh [EOF]

짧은 지식으로 od, awk, dd 등으로 스크립트를 만들어 테스트를 해보았는데
정확한 결과가 나오지 않고 있습니다. 그리고 이 사항에 대해 웹검색을 해보아도
바이너리 파일을 처리하는 정보는 얻을 수가 없었습니다.

부디 shell script에 조예가 있으신 분들의 조언 부탁드립니다.

-------------------------------------------------------------

auditory님 말씀도 그렇고 제가 작성한 소스를 올리는게 예의일 것 같아 소스를 올립니다.

# ...
SZ=`ls -l $1 | awk '{ print $5 }'`
 
SZ=`expr "$SZ" - 4`
 
# ... 
 
dd if=$1 of=/tmp/ftmp skip=1 bs=$SZ count=1 
 
BT=`od -x /tmp/ftmp | awk '{ if( $2 != "" ) { for( i=1 ; i <= 9 ; i++ ) { if( $i == "" ) { print $(i-2)$(i-1) break; } } } }'`
 
if [ "$BT" == ffffffff ]; then
    dd if=$1 of="$1_re" bs=$SZ count=1 
 
fi

지식이 부족하다 보니 질문드린 사항에 근접하지 못하고 파일 뒤 몇 바이트만 제거하는 기능밖에 작성하지 못 했습니다.

부디 눈에 불편함이 없으시기를... ^^;;

auditory의 이미지

작성하신 스크립트를 올리시고,
왜 잘안되는지를 문의하시면 좀더 빠른 답변을 얻으실 수 있을텐데요..

말씀하신 툴을 써서 아래와 같이 하면
끝에 ff가 몇 개있는지 알 수 있겠네요..
이 정보를 써서 dd로 자르시면 되겠습니다.

잘못된 점이나 더 나은 방법은 다른 분들께서..

od -t x1 -v input.bin | cut -b 9-  | tr -s ' ' '\n' | tac | awk '/[^f]/ { print NR-1; exit }'

--

아래 성능관련 이야기가 나와서요..
당연히 이 방법은 성능(속도/메모리 등)으로는 최악일겁니다.
말씀하신 여러가지 툴로 되나 안되나 *재미로* 해보는겁니다.. ^^

아래 aero님께서 테스트하신 방법으로 동일하게 해보면

aero:
real    0m1.201s
user    0m0.497s
sys     0m0.704s
 
auditory:
real    0m37.224s
user    0m34.185s
sys     0m2.978s

나오네요.. ^^ 제방법은 자르는 시간은 안 포함된겁니다.. ㅎㅎ

nagari의 이미지

우선 답글 감사합니다.

알려주신 스크립트를 이용해 테스트를 해보았는데 요구사항대로 동작하는걸
확인할 수 있었습니다.( 우선 성능면은 배제한 상태에서... )

[ auditory 님이 알려주신 스크립트 ] + dd 사용.

다시 한번 감사드립니다.

aero의 이미지

쉘스크립트의 범위가 어느정도 인지 모르겠지만
쉘스크립트라 함은 최대한 쉘의 기본 내부명령을 활용하는 것이 될텐데
auditory님이 적어주신 명령을 보면 독립적인 unix 명령이 5개나 들어가고
그만큼 5개의 프로세스가 떠서야 문제가 해결됩니다.
저런 다양한 명령이 시스템에 다 있느냐 OS별 명령의 옵션이나 동작방식이 다르지는 않는지
그런것도 고려되어야 하고요. 명령중에 하나의 독립적인 언어인 awk가 가지는 무게만 따져도 perl과 별다르지 않지요.

제가 말씀드리고 싶은 것은 쉘스크립트라는걸 그렇게 다양한 명령어를 조합해나가며
복잡하게 사용할 바에는 Perl이나 Python다른 스크립트 언어를 고려해보시는 것도 괜찮지 않겠느냐는겁니다.
요즘 리눅스에 Ruby는 몰라도 Perl,Python은 리눅스표준으로 다 기본 설치되니깐요.
Perl은 AIX,HP-UX,Solaris 같은 상용유닉스에서도 기본적으로 없는 경우를 보지도 못했고....

aero의 이미지

테스트 바이너리 생성

$ perl -e 'print "\xab\xcd\xef\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"' > binary
 
$ od -t x1 binary
0000000 ab cd ef ff ff ff ff ff ff ff ff ff ff ff ff
0000017

원하는 부분 제거

$ perl -p00i -e 's/\xff+$//' binary
 
$ od -t x1 binary
0000000 ab cd ef
0000003

위 간략한 perl코드를 설명하자면 -p -i 옵션이 파일자체를 수정하는 옵션이고 -00옵션은 라인베이스 처리가 아니라 파일을 통채로 slurp하라는 의미입니다. 코드의 s/\xff+$// 정규식은 파일끝의 연속된 \xff를 제거하는 거죠.

성능요? 100M 정도 더미 파일 만들어서 테스트하니 결과는 다음과 같습니다.

$ perl -e 'print( ("\xaa"x100000000)."\xab\xcd\xef\xff\xff\xff\xff\x0a\xff\xff\xff\xff\xff\xff\xff\xff")' > binary
$ ls -lah binary
-rw-r--r-- 1 aero aero 96M 2010-11-10 22:15 binary
$ time perl -p00i -e 's/\xff+$//' binary
 
real    0m0.540s
user    0m0.310s
sys     0m0.230s

cinsk 님 말을 듣고 seek,read,truncate같은 시스템 함수를 써서 테스트 해봤습니다. Python처럼이 아니라 Perl처럼? 했다는 것만 다를뿐 :)
C나 Python으로 해도 기본 로직은 별다를 것 없을것 같네요
[test.pl]

#!/usr/bin/env perl
use strict;
use warnings;
 
open my $fh, '+<', $ARGV[0];
seek $fh, 0, 2;
while ( tell $fh > 0 ) {
    seek $fh, -1, 1;
    read $fh, my $char, 1;
    if ( $char !~ "\xff" ) {
        truncate $fh, tell($fh);
        close $fh;
        last;
    }
    seek $fh, -1, 1;
}

$ time perl test.pl binary
 
real    0m0.006s
user    0m0.010s
sys     0m0.000s

비교가 안되게 빠르네요~ :)
처리해야될 파일이 많고 파일 크기가 테라 이상까지 가면 시스템함수를 직접사용하는 방법이 노력대비 효과를 얻을 수 있을듯 합니다.
기껏해야 몇KB~MB 정도면 간단한 방법을 놔두고 속도를 위해서 코딩시간을 희생하는 건 overkill인듯..
그리고 파일끝의 연속된 xff가 전체 파일크기에 비해 크기가 작다는 이상적인 가정하에서 빠른거지 그 크기가 큰 전체 파일크기의 반정도 이상이 넘어가거나 한다면 정규식으로 하나 seek으로 하나 별차이 없을 것 같네요.

nagari의 이미지

제가 Perl에 대해서는 더더욱 문외한이라 ... ^^;

그래도 조언 감사드리고 주신 정보는 잘 참고하도록 하겠습니다.

amorette의 이미지

이렇게 합니다.

perl -p00i -e 's{\xff*$}{}s' file

쓰고보니 aero님이 먼저 쓰셨네요 =3

cinsk의 이미지

대부분 shell programming에 쓰이는 script 언어로 위와 같은 문제를 해결하는 것은 바람직하지 않을 것 같습니다. UNIX(Linux)는 특별히 text/binary file을 별도로 취급하지는 않지만, 대개 binary file이라면 매우 큰 경우가 많은데, 이런 파일의 끝을 잘라내기 위해 script를 쓸 경우, 잘 작성하지 않으면, 전체 파일을 읽어와야하는 상황이 발생할 수 있기 때문입니다.

각 툴에서 regular expression을 어떻게 처리하느냐에 따라 달렸겠지만 말입니다. C 언어 등으로 작성하는게 귀찮다면 Python처럼 open하고 나서 seek()로 파일 뒷 부분으로 이동한 다음 작업하는 것이 효율적일 것 같네요. 예를 들면, 파일 포인터를 맨 뒤로 이동한 다음, 한 블럭 정도를 읽고 나서, 조사하는 방식을 쓰면 좋을 것 같네요.

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

amorette의 이미지

Python 처럼은 무슨뜻인가요?

cinsk의 이미지

amorette의 이미지

결론적으로 위와같은 문제를 스크립트 언어로 해결하는 것은 바람직하지 않은 것이 아닙니다.
seek 안되는 스크립트 언어가 어디 있겠습니까. (뭐 없진 않겠지만요. BrainF_ck 이라던가.)

자동화 작업에는 의외로 최적화된 성능보다 빠른 코드 작성이 먼저일 때도 있습니다.
결국 알고리즘 문제이기 때문에 어떤 언어로 작성하는 것이 더 바람직할 문제는 아니라고 봅니다.

nagari의 이미지

답글 감사합니다.

조언해주신 내용 잘 참고하도록 하겠습니다. ^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.