앗

길이가 같은 두 개의 데이터 행이 있다고 가정합니다.

abcdb#lae#blabl#a
abc~bola~xblabl~a

#첫 번째 줄의 문자(첫 번째 줄에는 하나 이상의 #이 있을 수 있음)를 삭제하고 다음 줄의 같은 위치에 있는 문자를 삭제해야 합니다 .

abcdblaeblabla
abc~bla~blabla

나는 이것을 시도했지만 sed '/#/{n;s/~//g}'내가 원하는 것보다 더 많은 문자를 제거합니다.

답변1

이러한 방법은 각 라인 쌍(1과 2, 3과 4 등)에 대해 반복되어 #각 쌍의 첫 번째 라인에 있는 문자 수만큼 처리하고 각 쌍의 두 라인이 동일하다고 가정합니다. 길이.

GNU awk(Linux) 및 BSD awk(Mac)와 호환됩니다.


하위 문자열을 사용하십시오.

awk '{ a=$0 ; gsub(/#/,"",$0) ; print $0 ; getline ; for (n=1;n<=length(a);n++) if ( substr(a,n,1) != "#" ) printf "%s",substr($0,n,1) ; printf "%s",RS }' file.txt

더 좁은 화면에 맞게 다시 포맷된 ​​동일한 코드:

awk '{
  a=$0 ;
  gsub(/#/,"",$0) ;
  print $0 ;
  getline ;
  for (n=1;n<=length(a);n++)
    if ( substr(a,n,1) != "#" )
      printf "%s",substr($0,n,1) ;
  printf "%s",RS
  }' file.txt
  • a=$0
    첫 번째 줄의 복사본을 저장합니다.
  • gsub(/#/,"",$0) ; print $0
    #복사본이 아닌 첫 번째 줄의 모든 항목을 삭제 하고 수정된 첫 번째 줄을 인쇄합니다.
  • getline
    다음 줄로 이동합니다.
  • for (n=1;n<=length(a);n++)
    복사본 첫 번째 줄의 각 문자를 단계별로 실행합니다.
    • if ( substr(a,n,1) != "#" )
      이 단일 문자 하위 문자열이 가 아닌 경우 #...
      • printf "%s",substr($0,n,1)
        ...그런 다음 두 번째 줄의 해당 위치에 문자를 인쇄합니다.
  • printf "%s",RS
    두 번째 줄을 개행 문자로 끝냅니다.

배열을 사용하십시오:

awk '{ c=d="" ; elements=split($0,a,"") ; getline ; split($0,b,"") ; for (n=1;n<=elements;n++) if (a[n]!="#") { c = c a[n] ; d = d b[n] } ; print c ; print d }' file.txt

더 좁은 화면을 위한 재포맷:

awk '{
  c=d="" ;
  elements=split($0,a,"") ;
  getline ;
  split($0,b,"") ;
  for (n=1;n<=elements;n++)
    if (a[n]!="#")
      { c = c a[n] ; d = d b[n] } ;
  print c ;
  print d
  }' file.txt
  • c=d=""
    두 개의 빈 문자열을 초기화합니다. 이는 입력 두 줄의 수정된 버전이 됩니다. 입력 라인 수가 2개를 초과하는 경우 이 단계가 필요합니다.
  • elements=split($0,a,"")
    입력의 첫 번째 줄을 배열 요소당 한 문자씩 배열로 변환합니다. 배열 요소의 개수를 변수로 저장합니다 elements.
  • getline
    다음 줄로 이동합니다.
  • split($0,b,"")
    입력의 두 번째 줄을 배열 요소당 문자가 하나씩 포함된 배열로 변환합니다.
  • for (n=1;n<=elements;n++)
    배열의 첫 번째 행에 있는 각 요소를 단계별로 실행합니다.
    • if (a[n]!="#")
      이 단일 문자 배열 요소가 가 아닌 경우 #...
      • { c = c a[n] ; d = d b[n] }
        ...그런 다음 두 줄 각각에 대해 문자를 위치에 유지합니다 n.
  • print c ; print d
    이 두 줄의 새 버전을 인쇄하세요.

경고하다:Mac(BSD) 버전의 awk는 배열 요소를 숫자순으로 자동 처리하지 않습니다. 이것은 처음에 나에게 놀라운 결과를 안겨주었습니다.

"for (indx in array)" 루프가 배열을 순회하는 순서는 POSIX awk에서 정의되지 않으며 구현마다 다릅니다. gawk를 사용하면 PROCINFO["sorted_in"]에 미리 정의된 특별한 값을 할당하여 순서를 제어할 수 있습니다.

GNU Awk 사용자 가이드

요소는 GNU awk처럼 1,2,3,...생성될 때 여전히 번호가 매겨져 있지만 BSD awk가 사용될 때 반드시 그 순서대로 표시되는 것은 아닙니다 . 따라서 잘못된 문자가 표시됩니다.splitfor (n in array)

이 문제를 해결하려면 예를 들어 배열을 만들 때 배열의 길이(요소 수)를 저장한 elements=split($0,a,"")다음 for (n=1;n<=elements;n++)여기에서 수행한 것처럼 요소에 대해 반복을 사용할 수 있습니다.


입력 예( file.txt):

abcdb#lae#blabl#a
abc~bola~xblabl~a
#alpha#beta#gamma#delta#epsilon#
abcdefghijklmnopqrstuvwxyzabcdef

출력 예:

abcdblaeblabla
abc~bla~blabla
alphabetagammadeltaepsilon
bcdefhijkmnopqstuvwyzabcde

답변2

다음과 같은 방법으로 sed를 사용하여 이 작업을 수행할 수 있습니다. 두 줄을 패턴 공간으로 가져온 후 두 줄의 시작 부분에 두 개의 마커를 배치합니다.

그런 다음 한 번에 한 문자씩 오른쪽으로 이동하기 시작합니다. 이동하는 동안 마커 오른쪽에 무엇이 있는지 확인하고 그에 따라 행동하십시오.

마커가 패턴 공간의 끝에 도달하면 중지합니다. 이제 표시된 작업이 완료되면 가져가십시오. 그러면 원하는 것이 무엇이든 남게 됩니다. 표시는 다음과 같습니다.\n

 sed -Ee '
   /#/N;/\n/!b
   s/\n/&&/;s/^/\n/
   :a
       /\n#(.*\n.*\n)./{
          s//\n\1/;ba
       }
      s/\n(.)(.*\n.*)\n(.)/\1\n\2\3\n/
   /\n$/!ba
   s/\n//;s///2
'    input

Perl 사용은 다음 아이디어에 따라 해결할 수 있습니다.

 perl -pe  ' 
     next unless /#/;

     my($n,$p) = (scalar <>);

     while ( /#/g ) {
        pos($n) = pos() - 1 - $p++;
        $n =~ s/\G.//;
     }

     y/#//d;s/\z/$n/;
'      input_file 

피복재:

1. Skip lines that donot have hash char.
 2. Save the next line in $n and init. $p counter which keeps track of the number of hash chars erased till now.
3.  Monitor the position of the hash char in a while loop and using info generate the position of the char to be deleted in next line.
4.  Erase it using the \G metachar in s///
5.  In the final step remove the hash chars from present line and append the next line to it.

이번에는 배열을 사용하는 또 다른 접근 방식이 나와 있습니다.

perl -aF'' -ne '
    print,next unless /#/;
    print,last if eof;

    my @I = grep { $F[$_] ne "#" } 0 .. $#F;
    my @N = split //, <>;

    print @F[@I], @N[@I];
'    input_file

피복재:

1. Invoke Perl to split each line on a per character basis and have it stored in the array @F anew for every line read.
2.  Record the array indices for which the array element is a non hash character.
3.  Readin the next line, split it on a per character basis and store in array @N.
4. Now its a matter of selecting the indices we stored in @I and fetch those from arrays @F and @N.

정규식 방법:

perl -pe '
   $_ .= <> unless eof;

    s/\G.(.*\n.{@{[+pos]}})./$1/ while /(?=#.*\n.)/g;
'        input_file

설명하다:

° 마지막 줄이 아닌 한 현재 줄에 다음 줄을 추가합니다.

° while 루프를 통해 첫 번째 줄에 해시 문자의 위치를 ​​기록합니다.

° 그런 다음 원래 줄의 해시 문자와 다음 줄의 해당 문자를 제거합니다.

° -p 옵션은 while 루프를 종료한 후 자동으로 $_를 표준 출력에 인쇄합니다.

순수 문자열 조작 방법:

perl -pe '
   last if eof;
   my $n = <>;
   while ( (my $p = index($_,"#")) > -1 ) {
      substr($_, $p, 1) = "" for $_, $n;
   }
   $_ .= $n;
'       input_file

여기에는 내장 인덱스를 사용하여 해시 위치를 확인한 다음 내장 substr에서 첫 번째 줄과 다음 줄에 두 번 사용하는 작업이 포함됩니다.

답변3

이것은 에 있습니다 awk. 그것을 볼 때 #, 그것이 줄의 어디에 있는지 결정하십시오. 그런 다음 해당 줄과 모든 후속 줄에 대해 해당 문자 위치가 줄에서 제거됩니다.

awk '
    /#/ { pound=index($0, "#") }
        {
                if (pound)
                        print substr($0, 1, pound-1) substr($0, pound+1)
                else
                        print
        }
    '

답변4

Gnu awk와 함께 gensub 사용

awk '
/#/{
  a=$0
  b=length()
  getline
  $0=a RS$0
  while($0!=a){
    a=$0
    $0=gensub("([^#]*)#(.{"b--"}).","\\1\\2",1)}
}1' infile

설명하다:

/#/: 각 줄에는 #이 있습니다.

a=$0: 행을

b=length(): b의 길이를 가져옵니다.

getline: 다음 줄을 가져옵니다

$0=a RS$0: a에 저장된 이전 행을 버퍼 $0의 시작 부분에 추가하고 그 뒤에 RS 레코드 구분 기호를 추가합니다.

이제 $0에는 2개의 행이 포함됩니다.

while($0!=a) : a에 저장된 행이 $0 버퍼와 다른 동안

a=$0: $0 버퍼를 가져옵니다.

$0=gensub("([^#]*)#(.{"b--"}).","\\1\\2",1): $0의 첫 번째 #과 해당 두 번째 줄을 삭제합니다. 문자

또한 첫 번째 줄의 길이를 1(b--) 줄입니다. 1#이 삭제되기 때문입니다.

1: 첫 번째 줄에 더 이상 #이 없으면 $0을 인쇄합니다.

관련 정보