여러 파일에서 줄을 읽을 때 sed의 $RANDOM 결과가 다릅니다.

여러 파일에서 줄을 읽을 때 sed의 $RANDOM 결과가 다릅니다.

디렉터리에 있는 여러 파일을 읽고, 각 파일의 각 줄을 살펴보고, 어떤 줄에서든 특정 문자열("gene_height")을 읽을 때마다 해당 줄의 2개 숫자를 바꾸는 명령을 실행하려고 합니다. 예를 들면 다음과 같습니다.

        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 192 "normal_height" 161 }

"gene_height"가 표시되고 192와 161을 170과 220 사이의 임의의 숫자로 바꿔야 하지만 "gene_height"로 시작하지 않기 때문에 이전 줄에서 60과 132는 그대로 두어야 합니다. 내가 현재 가지고 있는 것거의작동하지만 문제는 $RANDOM이 실제로 파일당 한 번만 실행되기 때문에 모든 항목에 동일한 숫자를 사용하고 한 줄에 작업하도록 분할하는 좋은 방법을 찾을 수 없다는 것입니다.

현재 나는 다음을 사용하고 있습니다 :

for f in *.txt; do sed -i -e '/\bgene_height\b/ s/ [0-9][0-9]*/ '$((169+RANDOM%51))'/g' $f; done

도움을 주시면 감사하겠습니다!

답변1

시도해 보세요 awk(sed 아님).

awk '/^gene_height/{
       $3=sprintf("%.0f",(169+rand()*5))
       $5=sprintf("%.0f",(169+rand()*5))
    }1' test*.txt

답변2

중괄호 안의 두 값은 항상 문자열 뒤에 오고 "normal_height"중괄호 안에는 아무것도 없다고 가정합니다.

(그런데, "normal_height"두 번 이상해 보이지만 이것이 귀하의 예제 텍스트에 있는 내용이므로 제가 사용할 것입니다.)

perl -pe 'BEGIN{ $gh_fmt = "gene_height={ \"normal_height\" %i  \"normal_height\" %i }" };
          s/gene_height\h*=\h*\{[^}]*\}/sprintf($gh_fmt, int(rand(51)+170), int(rand(51)+170))/e
          ' input.txt 

예제 출력:

                complexion={ "complexion_3" 60 "complexion_3" 132 }
                geneheight={ "normal_height" 216  "normal_height" 202 }

노트:

  • 이 옵션을 사용하면 입력의 각 줄을 읽고 각 줄에 코드를 적용한 다음 해당 줄을 인쇄하는 것과 유사한 작업이 -p수행됩니다 (수정 여부에 관계 없음).perlsed

  • BEGIN {}코드 블록은 다른 것을 실행하기 전에 한 번만 실행됩니다. 나머지 코드는 암시적 while-read 유형 루프의 각 입력 라인에 대해 한 번씩 실행됩니다.

  • 포함된 입력 줄에서 geneheight={중괄호 사이의 줄 부분을 원하는 텍스트로 대체합니다. 줄의 다른 내용은 변경되지 않습니다. 즉, 줄이 공백 한 개, 탭 한 개 또는 공백 한 개와 탭 두 개로 들여쓰기되는지 여부는 중요하지 않습니다. 들여쓰기되어 있는지, 패턴 앞이나 뒤에 같은 줄에 다른 텍스트가 있는지는 중요하지 않습니다 gene_height={...}.

  • \{[^}]*\}정규식 패턴의 일부가 {리터럴 문자, 0개 이상의 문자와 일치합니다.아니요a }, 그 뒤에 리터럴 }문자가 옵니다. 그것은 전체 {...}문자열입니다.

  • 검색 패턴은 \h*(0개 이상의 가로 공백 문자)를 사용하여 =기호 주위의 선택적 공백을 처리합니다. 이는 예제 입력에서 있는 그대로 작동하며 gene_height = {입력에 이와 유사한 내용이 포함된 경우에도 작동합니다. 출력은 항상 $gh_fmt문자열과 일치합니다(즉, 선택적 공백 없이).

  • 참고: 한 번에 한 줄만 읽는 이 특정 스크립트의 경우(가로 공백)는 (줄 바꿈, 폼 피드 등을 포함한 모든 공백) 과 똑같이 작동합니다 \h. \s그러나 Perl은 여러 줄의 입력을 처리할 수 있으므로 어쨌든 이 차이점을 기억할 가치가 있습니다. 보기 man perlre"Pattern White Space"검색

    전체 파일을 한 번에 수집하고 작업을 전체적으로 적용하는 스크립트 버전은 s///다음과 같습니다.

    perl -0777 -pe '
       BEGIN{ $gh_fmt = "geneheight={ \"normal_height\" %i  \"normal_height\" %i }" };
       s/gene_height\s*=\s*\{[^}]*\}/sprintf($gh_fmt, int(rand(51)+170), int(rand(51)+170))/mge
       ' input.txt
    

    이 버전은 줄 바꿈, 기호 앞이나 뒤, 문자열 내 =등 모든 종류의 공백이 포함된 입력을 처리할 수 있습니다 . 여러 줄 문자열에 대한 수정자와 전역 일치(즉, 입력에서 모든 항목 일치)를 위한 수정자가 {...}추가되었습니다.mg

  • Perl의 검색 및 대체 연산자 e수정자를 사용하십시오. s///이는 오른쪽(RHS)(대체 문자열)이 Perl 코드로 평가되도록 합니다. 즉, 함수를 실행합니다 sprintf(). 보기 man perlop및 검색"s/PATTERN/REPLACEMENT/msixpodualngcer"

  • 형식 문자열을 sprintf()함수에 직접 넣을 수 있습니다. 대신에 ( $gh_fmtBEGIN 블록에 정의된) 변수를 사용하기로 결정했는데, 그 이유는 그것이 더 읽기 쉽기 때문입니다. 또한 더 많은 패턴을 처리하기 위해 스크립트를 확장하는 방법(더 많은 형식 문자열과 더 많은 s///작업을 추가하기만 하면 됩니다)이 더 명확해집니다.

  • int(rand(51))0에서 50 사이의 난수를 반환합니다. 이것을 170에 추가하면 170과 220 사이의 임의의 숫자를 얻을 수 있습니다 perldoc -f rand. 보다.

  • 스크립트는 표준 입력 및/또는 하나 이상의 입력 파일을 사용합니다.

  • -iPerl의 내부 편집 옵션을 사용하면 표준 출력으로 인쇄하는 대신 입력 파일을 변경할 수 있습니다. man perlrun-i 작동 방식에 대한 자세한 내용은 참고자료를 참조하세요.

답변3

match()세 번째 인수 와 \<단어 경계 로 GNU awk(귀하의 질문에 분명히 GNU sed 스크립트를 사용하고 있기 때문에 사용했다고 가정합니다)를 사용하십시오 .

$ cat tst.awk
BEGIN { min=170; max=220; range=max-min+1; srand() }
match($0,/(.*\<gene_height=.* )[0-9]+(.* )[0-9]+(.*)/,a) {
    x = int( min + rand()*range )
    y = int( min + rand()*range )
    $0 = a[1] x a[2] y a[3]
}
{ print }

$ awk -f tst.awk file
        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 198 "normal_height" 183 }

$ awk -f tst.awk file
        complexion={ "complexion_3" 60 "complexion_3" 132 }
        gene_height={ "normal_height" 197 "normal_height" 205 }

"내부" 편집을 수행하려면 을 추가하면 됩니다. -i inplace물론 스크립트를 파일에 저장할 필요가 없으므로 다음을 수행할 수 있습니다.

awk -i inplace '
    BEGIN { min=170; max=220; range=max-min+1; srand() }
    match($0,/(.*\<gene_height=.* )[0-9]+(.* )[0-9]+(.*)/,a) {
        x = int( min + rand()*range )
        y = int( min + rand()*range )
        $0 = a[1] x a[2] y a[3]
    }
    { print }
' *.txt

관련 정보