2011-02-17 7 views
1

쉘 스크립트, awk 스크립트 및 find 명령의 조합을 사용하여 수백 개의 파일에서 여러 텍스트를 대체합니다. 파일 크기는 수백 바이트에서 20 킬로바이트 사이입니다.쉘 및 awk 스크립트 최적화

이 스크립트의 속도를 높이는 방법을 모색 중입니다.

cygwin을 사용하고 있습니다.

나는 수퍼 유저에게이 질문을 올렸지 만이 포럼이 더 적절하다고 생각합니다.

쉘 스크립트 -

#!/bin/bash 

if [ $# = 0 ]; then 
echo "Argument expected" 
exit 1 
fi 



while [ $# -ge 1 ] 
do 
    if [ ! -f $1 ]; then 
    echo "No such file as $1" 
    exit 1 
    fi 


    awk -f ~/scripts/parse.awk $1 > ${1}.$$ 

    if [ $? != 0 ]; then 
     echo "Something went wrong with the script" 
    rm ${1}.$$ 
     exit 1 
    fi 
mv ${1}.$$ $1 
shift 
done 

awk 스크립트 (간체) -

#! /usr/bin/awk -f 

/HHH.Web/{ 
    if (index($0,"Email") == 0) { 
     sub(/HHH.Web/,"HHH.Web.Email"); 
    } 
    printf("%s\r\n",$0); 
    next; 
} 

find . -type f | xargs ~/scripts/run_parser.sh 
+1

[교차 게시] (http://superuser.com/questions/246725/optimize-shell-and-awk-script)하지 마십시오. –

답변

2
find . -type f | while read -r file 
do 
    awk '/HHH.Web/ && !/Email/ { 
    sub(/HHH.Web/,"HHH.Web.Email"); 
    printf("%s\r\n",$0); 
    next; 
    } 
    ' "$file" > ${file}.$$ && mv ${file}.$$ "$file" 
done 

당신이 의지 지정된 파일을 알고있는 경우 명령 행 처리 중이라면을 추가 할 수 있습니다.옵션

+0

감사합니다. 그러나 더 빠르지는 않습니다. 몇 가지 빠른 테스트를 기반으로 한이 스크립트는 몇 퍼센트 느려집니다. – Bryan

1

각 파일에 대해 새로운 awk 프로세스가 생성됩니다. 나는

find . -type f | xargs ./awk_script.awk 

같은 것을 가질 수 있습니다 생각 어디 파일에 대한 awk_script.awk 검사 (나도 몰라 일반적인 관행). 아마도 mv $ {f}. $$ $ f를 할 수도 있었지만 bash와는 별도로 할 수 있습니다.

희망이 도움이됩니다.

+0

고마워, 나는 그것이 [link] (http://superuser.com/questions/246725/optimize-shell-and-awk-script/247275#247275)를 보았다고 생각했다. – Bryan

2

Cygwin에서 가장 중요한 것은 fork() - exec()를 가능한 많이 피하는 것입니다. desgin의 Windows는 Linux와 같은 여러 프로세스를 처리하도록 제작되지 않았습니다. fork()가 없으므로 암소가 부러졌습니다. 따라서 스크립트를 작성할 때 단일 프로세스에서 가능한 한 많이 수행하십시오.

이 경우 awk가 필요하고 awk 만 필요합니다. 모든 비용을 들이지 않고 xargs를 피하십시오. 또 다른 문제는 여러 파일을 검색해야하는 경우 Windows의 디스크 캐시가 농담 인 것입니다. 대신 모든 파일에 액세스 는 더 좋은 방법은, 당신이 열어야합니다 GREP은 그래서 당신은

grep -r "some-pattern-prahaps-HHH.Web-or-so" "/dir/to/where/you/have/millions/of/files/" |awk -f ~/scripts/parse.awk 

그리고 "~/스크립트/parse.awk"내 것 요구 을 부여 일치하는 파일 만 찾을 수있다 awk 내의 close() 파일을 사용하여 작업 속도를 향상시킬 수 있습니다. 가능한 한 system()을 사용하지 마십시오.

#!/bin/awk 
BEGIN{ 
    id=PROCINFO["pid"]; 
} 
# int staticlibs_codesize_grep(option, regexp, filepath, returnArray, returnArray_linenum ) 
# small code size 
# Code size is choosen instead of speed. Search may be slow on large files 
# "-n" option supported 
function staticlibs_codesize_grep(o, re, p, B, C, this, r, v, c){ 
if(c=o~"-n")C[0]=0;B[0]=0;while((getline r<p)>0){if(o~"-o"){while(match(r,re)){ 
B[B[0]+=1]=substr(r,RSTART,RLENGTH);r=substr(r,RSTART+RLENGTH);if(c)C[C[0]+=1]=c;} 
}else{if(!match(r,re)!=!(o~"-v")){B[B[0]+=1]=r;if(c)C[C[0]+=1]=c;}}c++}return B[0]} 
# Total: 293 byte , Codesize: > 276 byte, Depend: 0 byte 

{ 
    file = $0; 
    outfile = $0"."id; # Whatever. 
    # If you have multiple replacements, or multiline replacements, 
    # be carefull in the order you replace. writing a k-map for efficient condition branch is a must. 
    # Also, try to unroll the loop. 

    # The unrolling can be anyting, this is a trade between code size for speed. 
    # Here is a example of a unrolled loop 
    # instead of having while((getline r<file)>0){if(file~html){print "foo";}else{print "bar";};}; 
    # we have moved the condition outside of the while() loop. 
    if(file~".htm$"){ 
     while((getline r<file)>0){ 
      # Try to perform minimum replacement required for given file. 
      # Try to avoid branching by if(){}else{} if you are inside a loop. 
      # Keep it minimalit and small. 
      print "foo" > outfile; 
     } 
    }else{ 
     while((getline r<file)>0){ 
      # Here, as a example, we unrolled the loop into two, one for htm files, one for other files. 
      print "bar" > outfile; 
      # if a condition is required, match() is better 
      if(match(r,"some-pattern-you-want-to-match")){ 
       # do whatever complex replacement you want. We reuse the RSTART,RLENGTH from match() 
       before_match = substr(r,1,RSTART); 
       matched_data = substr(r,RSTART,RLENGTH); 
       after_match = substr(r,1,RSTART+RLENGTH); 
       # if you want further matches, like grep -o, extracting only the match 
       a=r; 
       while(match(a,re)){ 
        B[B[0]+=1]=substr(a,RSTART,RLENGTH); 
        a=substr(a,RSTART+RLENGTH); 
       } 
       # Avobe stores multiple matches from a single line, into B 
      } 
      # If you want to perform even further complex matches. try the grep() option. 
      # staticlibs_codesize_grep() handles -o , -n , -v options. It sould satisfy most of the daily needs. 
      # for a grep-like output, use printf("%4s\t\b:%s\n", returnArray_linenum[index] , returnArray[index]); 

      # Example of multiple matches, against data that may or may not been replaced by the previous cond. 
      if(match(r,"another-pattern-you-want-to-match")){ 
       # whatever 
       # if you decide that replaceing is not good, you can abort 
       if(for_whatever_reason_we_want_to_abort){ 
        break; 
       } 
      } 
      # notice that we always need to output a line. 
      print r > outfile; 
     } 
    } 
    # If we forget to close file, we will run out of FD 
    close(file); 
    close(outfile); 
    # now we can move the file, however I would not do it here. 
    # The reason is, system() is a very heavy operation, and second is our replacement may be imcomplete, by human error. 
    # system("mv \""outfile"\" \""file"\" ") 
    # I would advice output to another file, for later move by bash or any other shell with builtin mv command. 
    # NOTE[*1] 
    print "mv \""outfile"\" \""file"\" " > "files.to.update.list"; 
} 
END{ 
    # Assuming we are all good, we should have a log file that records what has been modified 
    close("files.to.update.list"); 
} 

# Now when all is ready, meaning you have checked the result and it is what you desire, perform 
# source "files.to.update.list" 
# inside a terminal , or 
# cat "files.to.update.list" |bash 
# and you are done 
# NOTE[*1] if you have file names containing \x27 in them, the escape with \x22 is incomplete. 
# Always check "files.to.update.list" for \x27 to avoid problems 
# prahaps 
# grep -v -- "`echo -ne "\x27"`" > "files.to.update.list.safe" 
# then 
# grep -- "`echo -ne "\x27"`" > "files.to.update.list.unsafe" 
# may be a good idea.