디렉터리에 있는 여러 파일을 읽고, 각 파일의 각 줄을 살펴보고, 어떤 줄에서든 특정 문자열("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
수행됩니다 (수정 여부에 관계 없음).perl
sed
이
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
이 버전은 줄 바꿈, 기호 앞이나 뒤, 문자열 내
=
등 모든 종류의 공백이 포함된 입력을 처리할 수 있습니다 . 여러 줄 문자열에 대한 수정자와 전역 일치(즉, 입력에서 모든 항목 일치)를 위한 수정자가{...}
추가되었습니다.m
g
Perl의 검색 및 대체 연산자
e
수정자를 사용하십시오.s///
이는 오른쪽(RHS)(대체 문자열)이 Perl 코드로 평가되도록 합니다. 즉, 함수를 실행합니다sprintf()
. 보기man perlop
및 검색"s/PATTERN/REPLACEMENT/msixpodualngcer"
형식 문자열을
sprintf()
함수에 직접 넣을 수 있습니다. 대신에 ($gh_fmt
BEGIN 블록에 정의된) 변수를 사용하기로 결정했는데, 그 이유는 그것이 더 읽기 쉽기 때문입니다. 또한 더 많은 패턴을 처리하기 위해 스크립트를 확장하는 방법(더 많은 형식 문자열과 더 많은s///
작업을 추가하기만 하면 됩니다)이 더 명확해집니다.int(rand(51))
0에서 50 사이의 난수를 반환합니다. 이것을 170에 추가하면 170과 220 사이의 임의의 숫자를 얻을 수 있습니다perldoc -f rand
. 보다.스크립트는 표준 입력 및/또는 하나 이상의 입력 파일을 사용합니다.
-i
Perl의 내부 편집 옵션을 사용하면 표준 출력으로 인쇄하는 대신 입력 파일을 변경할 수 있습니다.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