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


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.