문자열에서 모든 n번째 문자를 추출합니다.

문자열에서 모든 n번째 문자를 추출합니다.

해결책을 찾으려고 노력 중이에요이것질문. 지금까지 이 문제를 해결한 방법은 다음과 같습니다.

  • 모든 문자를 함께 추가하여 하나의 긴 문자열을 만듭니다.
  • 위 단계를 완료한 후 공백이나 탭 공백을 모두 제거하여 하나의 큰 문자열만 남게 합니다.

다음 명령을 사용하여 위 단계를 빌드할 수 있었습니다.

column -s '\t' inputfile | tr -d '[:space:]'

따라서 이와 같은 입력 파일의 경우

1   0   0   0   0   0

0   1   1   1   0   0

위 명령을 적용한 후 내 값은 다음과 같습니다.

100000011100

이제 이 큰 문자열에 다음과 같은 방법을 적용해 보겠습니다.

원래 OP에서 요구하는 대로 모든 6 자를 추출하여 문자열 끝까지 배열 요소에 추가합니다.

따라서 기본적으로 위의 단계를 통해 다음과 같이 배열 요소를 생성하려고 합니다.

10( 1번째 7번째 문자), ( 2번째018번째 문자 ), (3번째 9번째 문자), (4번째 10번째 문자), ( 5번째 11번째 문자 ), ( 6번째 와 12번째 문자) 첫 번째 문자).01010000

그래서 내 질문은, 추가 처리를 위해 배열에 추가할 수 있도록 모든 n 문자를 어떻게 추출할 수 있습니까? (이 경우 n=6).

답변1

두 줄

bash배열 을 생성 bash하는 순수한 솔루션은 다음과 같습니다 .

s="100000011100"
array=($(
    for ((i=0; i<${#s}-6; i++))
    do
        echo "${s:$i:1}${s:$((i+6)):1}"
    done
    ))
echo "${array[@]}"

그러면 질문에 표시된 것과 동일한 출력이 생성됩니다.

10 01 01 01 00 00

여기서 핵심 요소는 bash를 사용하는 것입니다.하위 문자열 확장. Bash를 사용하면 parametervia 와 같은 변수에서 하위 문자열을 추출할 수 있습니다 ${parameter:offset:length}. 우리의 경우 오프셋은 루프 변수에 의해 결정되며 i길이는 항상 입니다 1.

모든 라인 수에 대한 범용 솔루션

예를 들어 원래 문자열이 18자라고 가정하면 i의 i번째, i+6번째, i+12번째 문자를 0에서 5까지 추출하려고 합니다. 그래서:

s="100000011100234567"
array=($(
    for ((i=0; i<6; i++))
    do
        new=${s:$i:1}
        for ((j=i+6; j<${#s}; j=j+6))
        do 
            new="$new${s:$j:1}"
        done
        echo "$new"
    done
    ))

echo "${array[@]}"

그러면 다음과 같은 출력이 생성됩니다.

102 013 014 015 006 007

동일한 코드를 6자 라인으로 확장할 수 있습니다. 예를 들어 s세 줄(18자)이 있는 경우:

s="100000011100234567abcdef"

그러면 출력은 다음과 같습니다.

102a 013b 014c 015d 006e 007f

답변2

사용 perl:

$ echo 100000011100 | perl -nle '
    for ($i = 0; $i < length()/2; $i++) {
        print substr($_,$i,1), substr($_,$i+6,1);
    }
'
10
01
01
01
00
00

두 라인 모두에서 작동합니다. 임의의 행을 처리하려면 큰 문자열을 작성하는 대신 행을 직접 처리해야 합니다. 다음 입력을 전달하세요.

1   0   0   0   0   0                                                           
0   1   1   1   0   0                                                           
0   0   0   0   0   0

노력하다:

$ perl -anle '
    for ($i = 0; $i <= $#F; $i++) {
      push @{$h{$i}}, $F[$i];
    }
    END {
        print @{$h{$_}} for keys %h;
    }
' file
000
010
000
100
010
010

답변3

쉘 솔루션으로서 getopts아마도 가장 간단할 것입니다. 문제는 getoptsPOSIX가 지정되어 있고 정확히 원하는 작업을 수행한다는 것입니다. 즉, 쉘 루프에서 바이트 스트림을 처리합니다. 이상하게 들린다는 걸 알아요. 제가 이 말을 배우기 전에 여러분도 저와 같다면 다음과 같이 생각할 수도 있으니까요.글쎄요, 저는 그것이 명령줄 스위치를 처리해야 한다고 생각했습니다.이것은 사실이지만 첫 번째도 마찬가지입니다. 고려하다:

-thisisonelongstringconsistingofseparatecommandlineswitches

예, getopts이 문제를 해결해야 합니다. 루프에서 문자별로 분할하고 $OPTARG쉘 변수 또는 이름으로 지정한 다른 변수의 각 문자를 반환해야 하며, 이는 호출할 때 얼마나 구체적으로 얻는지에 따라 다릅니다. 더 중요한 것은 쉘 변수에 오류를 반환해야 한다는 것입니다.진행 상황을 저장하다쉘 변수에서 실행될 때 $OPTIND,중단한 부분부터 계속어떻게든 해결이 가능하다면. 하위 쉘을 호출하지 않고 전체 작업을 완료해야 합니다.

다음과 같은 결과가 있다고 가정합니다.

arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done

흠... 작동하는지 궁금해요?

echo "$((${#arg}/6))" "$#"
482 482

괜찮아...

eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4

따라서 보시다시피 이 getopts명령은 문자열의 6바이트마다 배열을 완전히 설정합니다. 이와 같은 숫자일 필요는 없으며 심지어 쉘 안전 문자일 ​​수도 있습니다. 위에서 했던 것처럼 대상 문자를 지정할 필요도 없습니다 01234565789. 나는 이것을 많은 쉘에서 반복적으로 테스트했으며 모두 잘 작동합니다. 몇 가지 특이한 점이 있습니다. bash첫 번째 문자가 공백 문자이면 버려집니다. 콜론 은 POSIX에서 명시적으로 금지하는 유일한 인수임에도 불구하고 지정된 인수로 dash허용됩니다 . :하지만 문제가 되지 않습니다. 오류를 반환하더라도 getopts현재 opt char 값은 계속 저장되기 때문입니다.$OPTARG(지정한 opt 변수에 할당된 ?로 표시됨)$OPTARG그렇지 않으면 옵션에 인수가 있어야 한다고 선언하지 않는 한 명시적으로 설정이 해제됩니다 . 공백은 좋은 것입니다. 공백은 하나만 버립니다.선두알 수 없는 값을 처리할 때 다음과 같이 할 수 있기 때문에 좋습니다.

getopts : o -" $unknown_value"

...첫 번째 문자가 실제로 허용하는 args 문자열에 있을 위험 없이 루핑을 시작합니다. 이렇게 하면 getopts전체 내용이 $OPTARG한 번에 인수로 삽입됩니다.

또 다른 예는 다음과 같습니다.

OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"                         
do printf '\\%04o' "'$OPTARG"; done  

\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166

$OPTIND=1방금 사용했기 때문에 첫 번째 줄에 설정했으며 , getopts재설정하기 전에 다음 호출이 중단된 부분부터 계속되기를 기대합니다. "${arg2}"즉, 원하는 것입니다. 하지만 주고 싶지 않고 지금은 다른 일을 하고 있어서 $OPTIND언제 시작할 수 있는지 알려드리기 위해 재설정했습니다.

내가 사용한 이 문자에서는 zsh선행 공백에 반대하지 않습니다. 따라서 첫 번째 문자는 8진수 40 공백 문자입니다. 하지만 저는 보통 getopts이런 식으로 사용하지 않습니다. 주로 사용합니다.피하다write()위에서 했던 것처럼 각 바이트에 대해 작업을 수행 하고 해당 출력(변수의)을 다른 쉘 변수에 할당합니다 set. 그런 다음 준비가 되면 전체 문자열을 가져올 수 있으며, 그렇게 하면 일반적으로 첫 번째 바이트가 제거됩니다.

답변4

sed내 마음에 가장 먼저 떠오른 것은.

$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz

5개의 문자를 일치시키고, 6번째 문자를 캡처하고, 모두 캡처된 문자로 대체합니다.

그러나 문자열 길이가 정확히 6의 배수가 아닌 경우 문제가 발생합니다.

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g' 
6bhntuvwxy

하지만 sed다음과 같이 약간 변경하면 이 문제를 해결할 수 있습니다.

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt

정규식의 욕심 ​​많은 특성으로 인해 가능하면 가변 길이 일치가 일치하고, 캡처할 항목이 남아 있지 않으면 캡처되지 않고 문자가 제거됩니다.

관련 정보