sed의 삽입 모드를 문자열 중간에 적용하는 것을 피하는 방법은 무엇입니까?

sed의 삽입 모드를 문자열 중간에 적용하는 것을 피하는 방법은 무엇입니까?

목적

목적은 다음 문자열을 변환하는 것입니다.

hello_hello,123-world567-helloworld123456,world1234-hello09876

특정 형식으로 변환하려면 sed를 사용하세요.

노력하다

sed -e 's|^\(hello_[a-z0-9]\{3\}\)\(.*\)|\1,\1\2|g;s|..|&/|g' /tmp/file

예상되는 결과

he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76/

현재 결과

문제는 /2자마다 a가 삽입된다는 점입니다. /두 개의 쉼표 사이에 삽입을 피하십시오.

he/ll/o_/he/ll/o,/12/3-/wo/rl/d5/67/-h/el/lo/wo/rl/d1/23/45/6,/wo/rl/d1/23/4-/he/ll/o0/98/76/

답변1

나는 이것을 할 수 있다:

sed 's|\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2|g
' <<\IN                                     
hello_hello,123-world567-helloworld123456,world1234-hello09876
IN

...인쇄...

/he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76

그래서최대두 번째 대체 항목이 변경되었습니다 s///. 하지만 이는 첫 번째 대체 항목을 모두 제거했기 때문입니다.

따라서 문제의 가장 큰 부분은 두 문자마다 sed하나씩 바꾸라고 말하는 것입니다 /.. 점은 다음을 의미합니다.모든 문자그리고 g글로벌 의미 - 또는모두.

두 번째로 중요한 부분은 첫 번째 대체가 도움이 되지 않으며 완전히 불필요하다는 것입니다.

뿐만 아니라 첫 번째 교체에 추가 쉼표를 삽입했습니다. 따라서 첫 번째 비트를 알아낸 후에도 여전히 추가 필드가 발생했습니다. 바라보다:

\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2

이것은 나에게 적합한 대체 설명이며 그 이유는 다음과 같습니다.

  • \(,[^,]*,\)\{0,1\}- 글로벌하게는 조심해서 필요한 만큼만 받아야 합니다. 두 문자마다 교체했으므로 다음과 같은 결과를 얻을 수 있습니다 sed.탐욕스러운. 이것을 먼저 인용하는 것이 중요합니다. 왜냐하면 sed왼쪽에서 오른쪽으로 읽을 때 일반적으로 쉼표가 아닌 두 개의 연속 문자 사이에 슬래시가 삽입되기 때문입니다. 그러나 쉼표가 발견되면 다음 쉼표를 읽고 저장합니다. \1슬래시를 전혀 삽입하지 않고 전체 블록을 삭제합니다.

  • \([^,]\{1,2\}\)- 여기에는 점을 사용할 수 없습니다 .. 쉼표와 일치하므로 구분 기호를 건너뛰고 슬래시만 입력하면 됩니다. 쉼표를 명시적으로 제외해야 합니다. 그것이 하는 일입니다 - 1개 또는 2개의 시퀀스마다 - sed항상 가능한 가장 큰 숫자를 가져옵니다.

이 예와 귀하의 예 사이에서 제가 볼 수 있는 한 가지 차이점은 여기서 첫 번째 슬래시가 문자열의 머리 부분에 있고 후행 슬래시가 없다는 것입니다. 반면에 귀하의 예에서는 그 반대입니다. 필요에 따라 이 문제를 해결하려면 다음을 수행하십시오.

...;s|^/\(.*/.\)/*$|\1/|...

답변2

나는 누군가가 순수한 접근 방식을 생각해 낼 것이라고 확신 sed하지만, 이런 종류의 작업에서는 단순한 행보다 입력 필드를 이해하는 프로그램을 사용하는 것이 훨씬 쉽다는 것을 알았습니다.

  1. 진주

    $ perl -F, -lane 'for($F[0],$F[2]){s|(..)|\1/|g;} print join ",",@F' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    설명하다

    • -a: 각 입력 행을 필드로 분할하고 @F배열에 저장합니다. 첫 번째 dt 필드는 첫 번째 dt 필드 $F[0], 두 번째 필드 $F[1]등이 됩니다 .
    • -F: 필드 구분 기호를 로 설정합니다 ,.
    • -n-e: 각 입력 라인( -n)을 읽고 에서 제공하는 스크립트를 적용합니다 -e.
    • -l:후행 줄 바꿈을 제거하고 \n각 호출 print에 a를 추가합니다 .
    • for($F[0],$F[2]){}:첫 번째와 세 번째 필드에 적용됩니다.
    • s|(..)|\1/|g;: 간단한 교체로, /한 문자씩 하나씩 추가됩니다.
    • print join ",",@F': 필드 목록을 쉼표로 연결하여 인쇄합니다. 이전 단계에서 필드가 변경되었으므로 변경된 필드가 인쇄됩니다.
  2. GNU awk

    $ awk -F, -v OFS="," '{$1=gensub(/(..)/,"\\1/","g",$1); $3=gensub(/(..)/,"\\1/","g",$3);}1;' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    설명하다

    위와 같이 -F필드 구분자를 설정합니다 . -v OFS=","출력 구분 기호를 로 설정합니다 ,. 그런 다음 gensub()함수(내가 믿는 GNU awk만)가 대체 작업을 실행합니다. 여기서는 첫 번째 및 세 번째 필드에서 작동합니다.

관련 정보