test.sh
다음 내용을 포함하는 bash 스크립트에 루프가 있습니다 .
#!/bin/bash
CHOSEN_NQUEUE=0
foo(){
for chunk in $(seq 0 $((${CHOSEN_NQUEUE}-1)));
do
echo "CHUNK = $(($chunk+1))"
done
}
bar(){
CHOSEN_NQUEUE=10
foo
}
bar
루프는 지금까지 잘 작동했습니다. 프로그램을 로 실행하면 . test.sh
루프에 다음 오류 코드가 표시됩니다.
-bash: 0
1
2
3
4
5
6
7
8
9+1: syntax error in expression (error token is "1
2
3
4
5
6
7
8
9+1")
프로그램을 다음과 같이 실행하면 bash test.sh
함수는 원하는 결과를 생성합니다.
CHUNK = 1
CHUNK = 2
CHUNK = 3
CHUNK = 4
CHUNK = 5
CHUNK = 6
CHUNK = 7
CHUNK = 8
CHUNK = 9
CHUNK = 10
이것은 더 큰 프로그램의 일부 bash program.sh
입니다 .
특히, 그냥 실행하면 foo
오류가 발생하지 않습니다. foo
에서 실행 하면 bar
오류가 발생합니다. bash program.sh
이는 또는 사용 여부에 관계없이 발생합니다 . program.sh
.
누구든지 내가 뭘 잘못하고 있는지 제안할 수 있나요? Bash의 다른 함수 내에서 함수를 실행하는 것은 나쁜 습관입니까?
진심으로 감사드립니다!
편집하다:
댓글을 남겨주신 모든 분들께 감사드립니다!
배열과 함께 select를 사용하여 문제가 발생했다는 것을 깨달은 후 다음 코드를 시도했습니다.
select opt in "${options[@]}"
do
next=false
local IFS=@
case "@${options[*]}@" in
(*"@$opt@"*)
foo
(*)
echo "Invalid option: $REPLY"
;;
esac
echo ""
done
echo "IFS = $IFS"
문제는 IFS=@
루프 외부에 @ 있어서는 안 된다는 사실에서 발생합니다.
그러나 이 코드를 실행하여 로컬로 설정하려고 하면 IFS
, 즉 전역적으로 수정되는 local IFS=@
것 같습니다 . IFS
코드 출력:
IFS = @
왜 이런 일이 일어나는지 아는 사람이 있나요?
다시 한 번 감사드립니다!
답변1
귀하가 받은 오류는 $chunk
여러 행의 값(모두 0에서 9까지의 숫자)이 있음을 나타냅니다. 단어 분할이 발생하면 이런 일이 발생합니다.아니요$(seq ...)
결과적으로 발생합니다 for
.
이제 단어 분리를 방지하는 일반적인 방법은 확장 주위에 큰따옴표를 넣어 for chunk in "$(seq ...)"
확장되지 않도록 하는 것입니다. 하지만 여기서는 그렇지 않습니다. 왜냐하면 큰따옴표를 추가하면 어쨌든 작동한다는 것을 알기 때문입니다.일부사례.
그러나 토큰화는 항상 동일하지는 않습니다. 값을 기반으로 하며 IFS
기본적으로 공백, 탭 및 줄바꿈( $' \t\n'
C 스타일 인용 사용)을 포함합니다. 다른 내용이 포함된 경우 해당 문자는 단어 구분 기호로 처리됩니다.
실제로 다음을 호출하기 전에 IFS
내부적으로 수정했습니다 .select
foo
local IFS=@
case "@${options[*]}@" in
(*"@$opt@"*)
foo
Bash에서 변수 범위가 작동하는 방식은 foo
수정된 값도 표시된다는 것입니다 IFS
. local
이는 변경 사항이 이 함수에만 표시되는 것이 아니라 이 수준에서 호출되는 모든 하위 함수에도 표시된다는 의미입니다.
$ x=999
$ a() { echo "a: x=$x"; }
$ b() { local x=$1; a; }
$ b 123
a: x=123
이는 C 언어의 상황과 다릅니다.
해결책은 IFS
다음과 같이 다른 변수에 저장하는 것입니다.
local oldifs=$IFS
IFS=@
str="@${options[*]}@"
IFS=$oldifs
case $str in ...
또는 서브셸에서 변경합니다(숨겨진 IFS
변경 사항).
str=$(IFS=@; echo "@${options[*]}@")
case $str in ...
문자열 연결을 수행하는 함수를 만들 수도 있습니다( IFS
함수의 변경 사항 숨기기). 변수를 이름으로 전달하려면 이름 참조만 있으면 됩니다.
# join a b c:
# join the values of array 'a' with the character 'b', and put the result in 'c'
join() {
local -n _arr=$1
local IFS=$2
local -n _res=$3
_res="${_arr[*]}"
}
src=(11 22 33)
join src @ dst
echo "$dst" # outputs "11@22@33"
(물론 이것은 한 가지 목적으로는 약간 다루기 힘들고 이름 참조는 완벽하지 않습니다. nameref~에함수는 동일한 이름의 변수를 참조할 수 없습니다.외부그렇습니다 (적어도 Bash 4에서는). 명령 대체를 사용하는 것보다 작은 이점은 하위 쉘을 시작하기 위해 포크를 사용하지 않는다는 것입니다. )
또는 안전을 위해 IFS
필요할 때마다 (재)설정하세요. 안에 foo
:
foo() {
local IFS=$' \t\n' # or just IFS=$'\n'
for chunk in $(seq ...); do
...
}