programing

Bash : 문자열을 문자 배열로 분할

firstcheck 2021. 1. 17. 10:56
반응형

Bash : 문자열을 문자 배열로 분할


구분 기호를 기반으로하지 않고 배열 인덱스 당 하나의 문자 만 기반으로하는 문자 배열로 분할하려는 Bash 셸 스크립트에 문자열이 있습니다. 어떻게 할 수 있습니까? 이상적으로는 외부 프로그램을 사용하지 않습니다. 다시 말하겠습니다. 내 목표는 이식성이므로 sedPOSIX 호환 시스템에있을 가능성이 높은 것은 괜찮습니다.


시험

echo "abcdefg" | fold -w1

편집 : 의견에 제안 된보다 우아한 솔루션을 추가했습니다.

echo "abcdefg" | grep -o .

배열 변환없이 이미 각 문자에 개별적으로 액세스 할 수 있습니다.

$ foo="bar"
$ echo ${foo:0:1}
b
$ echo ${foo:1:1}
a
$ echo ${foo:2:1}
r

충분하지 않다면 다음과 같이 사용할 수 있습니다.

$ bar=($(echo $foo|sed  's/\(.\)/\1 /g'))
$ echo ${bar[1]}
a

sed또는 그와 비슷한 것을 사용할 수 없다면 위의 첫 번째 기술을 원래 문자열의 길이 ( ${#foo})를 사용하여 while 루프와 결합 하여 배열을 만들 수 있습니다.

경고 : 문자열에 공백이 포함 된 경우 아래 코드가 작동하지 않습니다. Vaughn Cato의 대답 은 특별한 캐릭터로 살아남을 가능성이 더 높다고 생각 합니다.

thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))

문자열이 변수 x에 저장되면 개별 문자가 포함 된 배열 y가 생성됩니다.

i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1};  i=$((i+1));done

0 .. ${#string}-1for / while 루프 사용 하여 반복하는 대신 bash만으로이 작업을 수행 할 수있는 다른 두 가지 방법이 있습니다 . using =~및 using printf. ( eval{..}시퀀스 표현을 사용하는 세 번째 가능성 이 있지만 명확성이 부족합니다.)

bash에서 올바른 환경과 NLS를 사용하면 예상대로 비 ASCII에서 작동 sed하여 문제가되는 경우, 와 같은 이전 시스템 도구에서 잠재적 인 오류 원인을 제거합니다 . 이는 bash-3.0 (2005 년 출시)에서 작동합니다.

=~및 정규 표현식을 사용 하여 단일 표현식에서 문자열을 배열로 변환 :

string="wonkabars"
[[ "$string" =~ ${string//?/(.)} ]]       # splits into array
printf "%s\n" "${BASH_REMATCH[@]:1}"      # loop free: reuse fmtstr
declare -a arr=( "${BASH_REMATCH[@]:1}" ) # copy array for later

이것이 작동하는 방식은 확장을 수행하여 string각 단일 문자를으로 대체 (.)한 다음 생성 된 정규식을 그룹화와 일치시켜 각 개별 문자를로 캡처하는 것 BASH_REMATCH[]입니다. 인덱스 0은 전체 문자열로 설정됩니다. 특수 배열은 읽기 전용이므로 제거 할 수 없습니다 :1. 필요한 경우 인덱스 0을 건너 뛰도록 배열을 확장 할 때 주의하십시오 . 사소하지 않은 문자열 (> 64 자)에 대한 몇 가지 빠른 테스트는이 방법이 bash 문자열 및 배열 작업을 사용하는 방법 보다 훨씬 빠르다 는 것을 보여줍니다 .

위의 내용은 줄 바꿈이 포함 된 문자열과 함께 작동하며 기본적으로 NUL제외한 모든 항목과 일치=~ 하는 POSIX ERE를. 지원합니다 . 즉, 정규식은 REG_NEWLINE. (POSIX 텍스트 처리 유틸리티 의 동작은 이 점에서 기본적으로 다를 수 있으며 일반적으로 다릅니다.)

두 번째 옵션, 사용 printf:

string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do 
  ((xx)) && printf "\n" || break
done 

이 루프는 인덱스 ii증가 시켜 한 번에 한 문자를 인쇄하고 남은 문자가 없을 때 중단됩니다. bash printf가 오류 상태가 아닌 인쇄 된 문자 수 (C에서와 같이)를 반환 한 경우 대신 인쇄 된 문자 수 xx%n. (적어도 bash-2.05b까지는 작동합니다.)

bash-3.1을 사용 printf -v var하면 약간 더 융통성이 있으며 문자를 인쇄하는 것 이외의 작업을 수행하는 경우, 예를 들어 배열을 만드는 경우 문자열 끝에서 떨어지는 것을 방지 할 수 있습니다.

declare -a arr
ii=0
while printf -v cc "%s%n" "${string:(ii++):1}" xx; do 
    ((xx)) && arr+=("$cc") || break
done

string=hello123

for i in $(seq 0 ${#string})
    do array[$i]=${string:$i:1}
done

echo "zero element of array is [${array[0]}]"
echo "entire array is [${array[@]}]"

배열의 0 요소는입니다 [h]. 전체 배열은 [h e l l o 1 2 3 ]입니다.


가장 간단하고 완벽하며 우아한 솔루션 :

$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')  

및 테스트

$ echo ${ARRAY[0]}
  a

$ echo ${ARRAY[1]}
  b

설명 : read -astdin을 배열로 읽고 각 배열 항목의 구분자로 공백을 처리하는 변수 ARRAY에 지정합니다.

sed에 문자열을 에코하는 평가는 각 문자 사이에 필요한 공백을 추가합니다.

우리는 사용하고 여기에 문자열을 읽기 명령의 표준 입력을 공급하는 (<<<).


텍스트에 공백이 포함될 수있는 경우 :

eval a=( $(echo "this is a test" | sed "s/\(.\)/'\1' /g") )

$ echo hello | awk NF=NF FS=
h e l l o

또는

$ echo hello | awk '$0=RT' RS=[[:alnum:]]
h
e
l
l
o

이것을 배열에 저장하려면 다음을 수행하십시오.

string=foo
unset chars
declare -a chars
while read -N 1
do
    chars[${#chars[@]}]="$REPLY"
done <<<"$string"x
unset chars[$((${#chars[@]} - 1))]
unset chars[$((${#chars[@]} - 1))]

echo "Array: ${chars[@]}"
Array: f o o
echo "Array length: ${#chars[@]}"
Array length: 3

The final x is necessary to handle the fact that a newline is appended after $string if it doesn't contain one.

If you want to use NUL-separated characters, you can try this:

echo -n "$string" | while read -N 1
do
    printf %s "$REPLY"
    printf '\0'
done

AWK is quite convenient:

a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print $1,$2,$3}'

where FS and OFS is delimiter for read-in and print-out


For those who landed here searching how to do this in fish:

We can use the builtin string command (since v2.3.0) for string manipulation.

↪ string split '' abc
a
b
c

The output is a list, so array operations will work.

↪ for c in (string split '' abc)
      echo char is $c
  end
char is a
char is b
char is c

Here's a more complex example iterating over the string with an index.

↪ set --local chars (string split '' abc)
  for i in (seq (count $chars))
      echo $i: $chars[$i]
  end
1: a
2: b
3: c

zsh solution: To put the scalar string variable into arr, which will be an array:

arr=(${(ps::)string})

If you also need support for strings with newlines, you can do:

str2arr(){ local string="$1"; mapfile -d $'\0' Chars < <(for i in $(seq 0 $((${#string}-1))); do printf '%s\u0000' "${string:$i:1}"; done); printf '%s' "(${Chars[*]@Q})" ;}
string=$(printf '%b' "apa\nbepa")
declare -a MyString=$(str2arr "$string")
declare -p MyString
# prints declare -a MyString=([0]="a" [1]="p" [2]="a" [3]=$'\n' [4]="b" [5]="e" [6]="p" [7]="a")

As a response to Alexandro de Oliveira, I think the following is more elegant or at least more intuitive:

while read -r -n1 c ; do arr+=("$c") ; done <<<"hejsan"

ReferenceURL : https://stackoverflow.com/questions/7578930/bash-split-string-into-character-array

반응형