Bash에서 문자열의 각 문자에 대해 for 루프를 수행하는 방법은 무엇입니까?
다음과 같은 변수가 있습니다.
words="这是一条狗。"
각 캐릭터에 대해 한 번에 하나씩 포 루프를 만들고 싶습니다. 예를 들어, 첫 번째character="这",그리고나서character="是",character="一",기타.
내가 아는 유일한 방법은 각 문자를 파일의 별도 행으로 출력한 다음 사용하는 것입니다.while read line하지만 이것은 매우 비효율적으로 보입니다.
- for 루프를 통해 문자열의 각 문자를 처리하려면 어떻게 해야 합니까?
C 스타일을 사용할 수 있습니다.for루프:
foo=string
for (( i=0; i<${#foo}; i++ )); do
echo "${foo:$i:1}"
done
${#foo}의 길이로 확장됩니다.foo.${foo:$i:1}위치에서 시작하여 하위 문자열로 확장됩니다.$i길이 1의
와 함께sed에dash의 껍질.LANG=en_US.UTF-8다음이 올바르게 작동합니다.
$ echo "你好嗎 新年好。全型句號" | sed -e 's/\(.\)/\1\n/g'
你
好
嗎
新
年
好
。
全
型
句
號
그리고.
$ echo "Hello world" | sed -e 's/\(.\)/\1\n/g'
H
e
l
l
o
w
o
r
l
d
따라서 출력은 다음으로 루프될 수 있습니다.while read ... ; do ... ; done
샘플 텍스트를 영어로 번역하기 위해 편집됨:
"你好嗎 新年好。全型句號" is zh_TW.UTF-8 encoding for:
"你好嗎" = How are you[ doing]
" " = a normal space character
"新年好" = Happy new year
"。全型空格" = a double-byte-sized full-stop followed by text description
${#var}의 길이를 반환합니다.var
${var:pos:N}N자를 반환합니다.pos앞으로
예:
$ words="abc"
$ echo ${words:0:1}
a
$ echo ${words:1:1}
b
$ echo ${words:2:1}
c
그래서 반복하기 쉽습니다.
다른 방법:
$ grep -o . <<< "abc"
a
b
c
또는
$ grep -o . <<< "abc" | while read letter; do echo "my letter is $letter" ; done
my letter is a
my letter is b
my letter is c
아무도 명백한 사실을 언급하지 않은 것이 놀랍습니다.bash만을 활용하는 솔루션while그리고.read.
while read -n1 character; do
echo "$character"
done < <(echo -n "$words")
의 사용에 주의합니다.echo -n마지막에 불필요한 새로운 선을 피하기 위해. printf이는 또 다른 좋은 옵션이며 특정 요구사항에 더 적합할 수 있습니다.공백을 무시하려면 대체"$words"와 함께"${words// /}".
또 다른 옵션은fold단, for 루프에 절대 입력해서는 안 됩니다.대신 다음과 같이 시간 루프를 사용합니다.
while read char; do
echo "$char"
done < <(fold -w1 <<<"$words")
외장형 제품을 사용할 경우의 주요 이점fold(코어유틸리티 패키지의) 명령은 간결함일 것입니다.출력을 다음과 같은 다른 명령으로 제공할 수 있습니다.xargs(findutils 패키지의 일부)는 다음과 같습니다.
fold -w1 <<<"$words" | xargs -I% -- echo %
당신은 그것을 교체하고 싶을 것입니다.echo각 문자에 대해 실행할 명령과 함께 위의 예제에서 사용된 명령입니다.참고:xargs기본적으로 공백을 삭제합니다.사용할 수 있습니다.-d '\n'그 동작을 비활성화합니다.
국제화
방금 테스트했습니다.fold일부 아시아 문자와 함께 유니코드를 지원하지 않는다는 것을 깨달았습니다.따라서 ASCII 요구사항에는 문제가 없지만 모두에게 효과가 있는 것은 아닙니다.그런 경우에는 몇 가지 대안이 있습니다.
아마 제가 대신할 겁니다fold -w1어색한 배열로:
awk 'BEGIN{FS=""} {for (i=1;i<=NF;i++) print $i}'
아니면grep다른 답변에 언급된 명령:
grep -o .
성능
참고로, 저는 앞서 언급한 세 가지 옵션을 벤치마킹했습니다.처음 두 개는 빠르며 거의 동점에 가까웠고, 폴드 루프는 도중 루프보다 약간 더 빠릅니다.당연하지도 않게xargs가장 느린...75배 더 느립니다.
다음은 (약어) 테스트 코드입니다.
words=$(python -c 'from string import ascii_letters as l; print(l * 100)')
testrunner(){
for test in test_while_loop test_fold_loop test_fold_xargs test_awk_loop test_grep_loop; do
echo "$test"
(time for (( i=1; i<$((${1:-100} + 1)); i++ )); do "$test"; done >/dev/null) 2>&1 | sed '/^$/d'
echo
done
}
testrunner 100
결과는 다음과 같습니다.
test_while_loop
real 0m5.821s
user 0m5.322s
sys 0m0.526s
test_fold_loop
real 0m6.051s
user 0m5.260s
sys 0m0.822s
test_fold_xargs
real 7m13.444s
user 0m24.531s
sys 6m44.704s
test_awk_loop
real 0m6.507s
user 0m5.858s
sys 0m0.788s
test_grep_loop
real 0m6.179s
user 0m5.409s
sys 0m0.921s
모든 공백 문자를 올바르게 보존하고 충분히 빠른 이상적인 솔루션은 아직 없다고 생각하므로 답변을 올리겠습니다.용사를 합니다.${foo:$i:1}작동하지만 속도가 매우 느리며, 아래에서 보여드릴 것처럼 큰 문자열에서 특히 두드러집니다.
제 아이디어는 식스가 제안한 방법의 확장입니다.read -n1모든 문자를 유지하고 모든 문자열에 대해 올바르게 작동하도록 몇 가지 변경 사항 포함:
while IFS='' read -r -d '' -n 1 char; do
# do something with $char
done < <(printf %s "$string")
작동 방식:
IFS=''내부 필드 구분 기호를 빈 문자열로 재정의하면 공백과 탭이 제거되지 않습니다.▁as와 같은 줄에서 것.read다른 셸 명령에 영향을 미치지 않음을 의미합니다.-r"원시"를 의미하며, 이는 다음을 방지합니다.read치료로부터.\줄의 끝에 특수 줄 연결 문자로 표시됩니다.-d ''하면 구분기사없수빈전문이 방지됩니다.read새 줄 문자를 제거할 수 없습니다.실제로는 null 바이트가 구분 기호로 사용됨을 의미합니다.-d ''와같과 .-d $'\0'.-n 1한 번에 하나의 문자를 읽음을 의미합니다.printf %s "$string"사용printf에echo -n더 안전합니다, 왜냐하면echo식간-n그리고.-e옵으로션면 하면 "-e"가 됩니다.echo아무것도 인쇄하지 않습니다.< <(...)프로세스 대체를 사용하여 루프에 문자열을 전달합니다.대신 한다면 (여기서 문자열을 사용합니다.done <<< "$string"됩니다.), 마막에바문추자가됩다니가꿈줄지▁),다니▁character▁an▁is추가됩line.또한, 파이프를 통해 문자열 전달(printf %s "$string" | while ...하위 되며, 는 모든 이라는 것을 을 사용하면 루프가 하위 셸에서 실행됩니다. 즉, 모든 변수 작업이 루프 내에서 로컬입니다.
이제 큰 끈으로 성능을 테스트해 보겠습니다.다음 파일을 소스로 사용했습니다.
https://www.kernel.org/doc//kbuild/makefiles.txthttps ://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
는 다음스가호다니습출었을 통해 되었습니다.time명령:
#!/bin/bash
# Saving contents of the file into a variable named `string'.
# This is for test purposes only. In real code, you should use
# `done < "filename"' construct if you wish to read from a file.
# Using `string="$(cat makefiles.txt)"' would strip trailing newlines.
IFS='' read -r -d '' string < makefiles.txt
while IFS='' read -r -d '' -n 1 char; do
# remake the string by adding one character at a time
new_string+="$char"
done < <(printf %s "$string")
# confirm that new string is identical to the original
diff -u makefiles.txt <(printf %s "$new_string")
결과는 다음과 같습니다.
$ time ./test.sh
real 0m1.161s
user 0m1.036s
sys 0m0.116s
우리가 볼 수 있듯이, 그것은 꽤 빠릅니다.
다음으로 루프를 매개 변수 확장을 사용하는 루프로 교체했습니다.
for (( i=0 ; i<${#string}; i++ )); do
new_string+="${string:$i:1}"
done
출력은 성능 손실이 얼마나 심각한지 정확히 보여줍니다.
$ time ./test.sh
real 2m38.540s
user 2m34.916s
sys 0m3.576s
정확한 숫자는 시스템에 따라 매우 다를 수 있지만 전체적인 그림은 비슷해야 합니다.
ASCII 문자열로만 테스트했지만 다음과 같은 작업을 수행할 수 있습니다.
while test -n "$words"; do
c=${words:0:1} # Get the first character
echo character is "'$c'"
words=${words:1} # trim the first character
done
▁into▁using▁▁it다▁ 사용하여 을 문자 배열로 분할할 수도 있습니다.fold합니다.
for char in `echo "这是一条狗。" | fold -w1`; do
echo $char
done
@에 있는 C 는 셸 @chepner 답에는있 C 스일셸함있습다니에에 .update_terminal_cwd 리고그고.grep -o .솔루션은 영리하지만, 사용하는 솔루션을 보지 못해서 놀랐습니다.seq내 것은 다음과 같습니다.
read word
for i in $(seq 1 ${#word}); do
echo "${word:i-1:1}"
done
#!/bin/bash
word=$(echo 'Your Message' |fold -w 1)
for letter in ${word} ; do echo "${letter} is a letter"; done
다음은 출력입니다.
Y는 문자 o는 문자 u는 문자 r은 문자 M은 문자 e는 문자 e는 문자 a는 문자 g는 문자 e는 문자
POSIX 호환 셸에서 ASCII 문자를 반복하려면 매개 변수 확장을 사용하여 외부 도구를 피할 수 있습니다.
#!/bin/sh
str="Hello World!"
while [ ${#str} -gt 0 ]; do
next=${str#?}
echo "${str%$next}"
str=$next
done
또는
str="Hello World!"
while [ -n "$str" ]; do
next=${str#?}
echo "${str%$next}"
str=$next
done
유니코드가 있는 sed 작품들
IFS=$'\n'
for z in $(sed 's/./&\n/g' <(printf '你好嗎')); do
echo hello: "$z"
done
산출물
hello: 你
hello: 好
hello: 嗎
공백이 무시되는 것에 대해 신경 쓰지 않는 경우 다른 접근 방식:
for char in $(sed -E s/'(.)'/'\1 '/g <<<"$your_string"); do
# Handle $char here
done
다른 방법은 다음과 같습니다.
Characters="TESTING"
index=1
while [ $index -le ${#Characters} ]
do
echo ${Characters} | cut -c${index}-${index}
index=$(expr $index + 1)
done
fold그리고.while read여기 몇 가지 답변에 나와 있는 것처럼 업무에 적합합니다.이러한 답변과는 달리 실행 순서에 따라 파이프를 연결하는 것이 훨씬 직관적이라고 생각합니다.
echo "asdfg" | fold -w 1 | while read c; do
echo -n "$c "
done
출력:a s d f g
솔루션을 공유합니다.
read word
for char in $(grep -o . <<<"$word") ; do
echo $char
done
TEXT="hello world"
for i in {1..${#TEXT}}; do
echo ${TEXT[i]}
done
{1..N}입니다.
${#TEXT} 수입니다.
${TEXT[i]} 수 .
언급URL : https://stackoverflow.com/questions/10551981/how-to-perform-a-for-loop-on-each-character-in-a-string-in-bash
'programing' 카테고리의 다른 글
| 어떤 iOS SDK가 있는지 어떻게 확인합니까? (0) | 2023.05.04 |
|---|---|
| 수학 간의 차이.바닥() 및 수학.잘라내기() (0) | 2023.05.04 |
| Python 유형 힌트: 입력.매핑 대 입력.딕트 (0) | 2023.05.04 |
| 브라우저에 잘못된 파일 저장소 URL이 표시됨머리글 값 (0) | 2023.04.29 |
| WPF GUI에서 비동기 작업을 실행하고 상호 작용하는 방법 (0) | 2023.04.29 |