문자열을 순차 색인으로 교체


10

누군가 이것을 달성하는 우아한 방법을 제안 할 수 있습니까?

입력:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

출력은 다음과 같아야합니다.

test      instant1  ()

test      instant2  ()

test      instant1000()

빈 줄은 입력 파일에 있으며 한 번에 처리 해야하는 동일한 디렉토리 아래에 많은 파일이 있습니다.

나는 같은 디렉토리에있는 많은 파일을 대체하려고 시도했지만 작동하지 않았습니다.

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

오류 :

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

그리고 나는 이것을 시도했다 :

perl -i -pe 's/instant/$& . ++$n/ge' *.vs

그것은 효과가 있었지만 인덱스는 한 파일에서 다른 파일로 계속 증가했습니다. 새 파일로 변경하면 1로 재설정하고 싶습니다. 좋은 제안이 있습니까?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

작동하지만 다른 모든 파일을 교체해서는 안됩니다. 파일 만 바꾸는 것을 선호합니다 *.txt.


그리고 그들은 모두 빈 줄로만 구성 test instant ()됩니까?
terdon

이중 간격의 줄을 다시 넣으면 종종이 사이트의 마크 업을 사용하는 방법을 모르는 새로운 사용자의 징조입니다. 따라서 파일 내용으로 표시되도록 파일 내용 블록을 들여 쓰기하면서 terdon이 제거했습니다. 희망은 지금 괜찮습니다.
Timo

답변:


14
perl -pe 's/instant/$& . ++$n/ge'

또는 GNU awk:

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

내부에서 파일을 편집하려면 다음 -i옵션을 추가하십시오 perl.

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*.vs

또는 재귀 적으로 :

find . -name '*.vs' -type f -exec perl -pi -e '
  s/instant/$& . ++$n{$ARGV}/ge' {} +

설명

perl -pe 's/instant/$& . ++$n/ge'

-p입력을 한 줄씩 처리하고 -e각 줄에 전달 된 표현식을 평가하여 인쇄하는 것입니다. 각 줄마다 ( s/re/repl/flags연산자를 사용하여 ) instant자체 ( $&)와 증가 된 변수 값을 대체 합니다 ++$n. g플래그가 전 세계적으로 (다만 한 번)를 교체하고, e그래서 교체에 펄 코드로 해석되는 전자 평가할 (안 고정 문자열).

하나의 perl 호출이 둘 이상의 파일을 처리하는 전체 편집 $n을 위해 각 파일에서 재설정 하려고 합니다. 대신 $n{$ARGV}( $ARGV현재 처리 된 파일이있는 곳 )을 사용합니다.

awk사람은 약간의 설명을 가치가있다.

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

우리는 GNU의 기능을 사용하여 awk임의의 문자열 (정규식조차도)에 대한 레코드를 분리합니다. 를 사용 -vRS=instant하여 r̲ecord s̲eparator 를로 설정 했습니다 instant. RT일치 무슨 보유하고있는 변수입니다 RS, 그래서 일반적으로 instant는 빈 문자열입니다 마지막 레코드를 제외하고는. 위의 입력에서 레코드 ( $0) 및 레코드 종결 자 ( RT)는 ( [$0|RT])입니다.

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

따라서 첫 번째 레코드를 제외한 모든 레코드의 시작 부분에 증분 번호를 삽입하기 만하면됩니다.

우리가 위에서하는 일입니다. 첫 번째 레코드 n는 비어 있습니다. ORS ( o̲utput r̲ecord s̲eparator )를 RT로 설정하여 awk 인쇄합니다 n $0 RT. ++n항상 true (0이 아닌 숫자)로 평가되는 조건 인 두 번째 표현식 ( )에서 수행되므로 $0 ORS모든 레코드에 대해 기본 인쇄 (인쇄 )가 수행됩니다.



5

sed실제로 작업에 가장 적합한 도구는 아니므로 더 나은 스크립팅 기능을 갖춘 무언가를 원합니다. 다음은 몇 가지 선택 사항입니다.

  • perl -00pe 's/instant/$& . $./e' file 

    -p모든 스크립트를 적용한 후 "모든 줄을 인쇄"를 의미 -e합니다. -00기록 (선) 그래서 "단락 모드"에 회전이 연속으로 줄 바꿈에 의해 정의된다 ( \n) 자,이 제대로 이중 간격 라인을 처리 할 수 있습니다. $&일치하는 마지막 패턴이며 $.입력 파일의 현재 줄 번호입니다. e에서이 s///e나를 대체 연산자 식을 평가 할 수 있습니다.

  • awk (데이터가 세 개의 공백으로 구분 된 필드로 표시되어있는 것으로 가정)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    여기서 우리 는 현재 행이 비어 있지 않은 경우에만 k변수를 증가시킵니다. 이 경우 필요한 정보도 인쇄합니다. 빈 줄은 그대로 인쇄됩니다.k/./

  • 다양한 껍질

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 
    

    여기에서 각 입력 라인은 공백으로 자동 분할되고 필드는 $a, $b및 로 저장됩니다 $c. 그런 다음 루프 내에서 비어 있지 않은 $c각 줄에 대해 1 씩 증가되고 $a현재 값이 두 번째 필드 옆에 인쇄 $b됩니다.

참고 : 위의 모든 솔루션 은 파일의 모든 행이 동일한 형식 이라고 가정 합니다. 그렇지 않다면 @Stephane의 대답이 갈 길입니다.


많은 파일을 처리 하고 현재 디렉토리의 모든 파일에 대해이 작업을 수행하려는 경우 다음을 사용할 수 있습니다.

for file in ./*; do perl -i -00pe 's/instant/$& . $./e' "$file"; done

조심 : 필요가 더 복잡한 일을 처리 갈 (가정 할 경우 즉, 공백없이 간단한 파일 이름을 가정 ksh93, zsh또는 bash) :

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -00pe 's/instant/$& . $./e' "$file"
done

펄 스크립트가 작동합니다. 그러나 선이 이중 공간 인 경우 작은 문제가 있습니다.
user3342338

@ user3342338 예, 현재 줄 번호를 사용하고 있으므로 카운터가 증가합니다. 내가 Stephane의 것이 더 강력하다고 말했듯 이 이것은 매우 순진한 접근법입니다. 빈 줄이 있거나 표시 한 줄에서 벗어난 줄이 있으면 이러한 작업이 작동하지 않습니다.
terdon

@ user3342338 업데이트 된 답변을 참조하십시오. 그들은 모두 이중 간격 파일에서 작동합니다.
terdon

훌륭한 답변과 대체 방법의 선택 !! 감사합니다
Madivad

0

이 문제를 해결 sed하려면 다음과 같은 것을 사용할 수 있습니다 bash.

i=0
while read -r line; do
  sed "s/\(instant\)/\1${i}/" <<< "${line}"
  [[ ${line} =~ instant ]] && i=$(( i + 1 ))
done < file

또는 더 휴대용 솔루션은 다음과 같습니다.

i=0
while read -r line; do
  echo "${line}" | sed "s/\(instant\)/\1${i}/"
  if echo "${line}" | grep -q inst; then
    i=$(( i + 1 ))
  fi
done < file
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.