산술 확장과 중괄호 확장을 결합할 수 있나요?
$ for i in {$((1 + 1))..5}; do echo $i; done;
{2..5}
$ echo "bash laughs at me"
답변1
man bash
설명은 다음을 참조하세요.
확장 순서는 중괄호 확장, 물결표 확장, 매개변수, 변수 및 산술 확장, 명령 대체(왼쪽에서 오른쪽으로 수행), 토큰화 및 경로 이름 확장입니다.
중괄호 확장은 산술 확장 전에 발생하므로 원하는 방식으로 결합할 수 없습니다.
대신 사용하십시오 seq
:
for i in $(seq $((a+4)) 12) ; do echo $i ; done
답변2
eval 'for i in {'"$((1 + 1))"'..5}; do echo $i; done'
산출
2
3
4
5
일반적으로 말하면, 실제로 수행하기 전에 쉘이 어떤 작업을 수행하도록 하려면 쉘이 다시 확인하도록 해야 합니다. 이것이 eval
함수입니다. 명령을 두 번 평가합니다. 쉘은 쉘 확장에 대한 평가를 구문 분석합니다. 이는 일반적으로 발생하지 않습니다.
생각해 보세요:
v='"quotes in here"'; printf %s\\n $v
"quotes
in
here"
입력을 처리하는 쉘의 파서가 따옴표를 해석하는 것을 볼 수 있습니다.앞으로확장되어 그 안의 따옴표가 $v
의미가 없습니다. 이를 이해할 수 있는 파서가 없습니다. 하지만 이렇게 하면:
v='"quotes in here"'; eval "printf '%s\\n' $v"
quotes in here
출력이 갑자기 달라졌습니다. 차이점은 파서입니다. 입력의 어떤 비트가 명령인지, 그 이유는 무엇인지 결정합니다. 이것은 ;${}()||''""&& while for if
이 모든 것과 관련된 부분입니다. 이것은 {}
다음 중괄호가 귀하가 요청한 것과 동일하다는 것을 의미하지는 않습니다 . 다른 답변에서 볼 수 있듯이 이것들은 분명히 bash
확장으로 별도로 처리됩니다. 그럼에도 불구하고 일반적으로 이 문제를 처리하려면 일종의 두 번째 평가가 필요합니다.
이것이 eval
위험을 만드는 것입니다. 확장을 참조 하면 "${expansion}"
이를 파서로 표시합니다.한계그것. 쉘은 무슨 일이 일어나든 이것이 명령의 하나의 항목일 뿐이라는 것을 알고 있습니다(인수일 수도 있음). 그렇지 않더라도 쉘은 간단한 명령의 경계를 $expansion
넘어서는 명령을 허용하지 않습니다. 왜냐하면 그 간단한 명령은 이미;
분리된. 그러나 - 그렇듯이 eval
- 쉘이 돌아가서 확장을 다시 보면 실행할 수 있는 명령을 찾을 수 있습니다.몇 개라도- eval
작동 방식은 이렇습니다.
따라서 쉘 토큰을 사용할 때 eval
실수로 두 번 평가하지 않도록 매우 주의해야 합니다.또는- 그렇지 않으면 쉘이 충분히 빨리 완료할 수 없는 첫 번째 루프의 부분만 평가합니다. 모범 사례는 아래와 같이 서로 다른 시점에 명령의 한 부분만 개별적으로 평가하는 것입니다.
이것은 eval
다시 첫 번째 문자열입니다.
eval \ #inital command
'for i in {'\ #hard-quoted - not expanded or executed
"$((1 + 1))"\ #double-quoted - expanded and delimited
'..5}; do echo $i; done' #hard-quoted - not expanded or executed
위의 의견은 첫 번째 라운드에서 취한 조치에 대해 논의합니다 eval
. 아직 한 단계 더 남았습니다. 이 경우에는 을 원하므로 1+1
먼저 확장하고 다른 것은 확장하지 않습니다. 우리가 피할 수 있는 최악의 상황은 2
. 이는 쉘 파서 토큰이 아니며 큰따옴표 내에서도 안전하게 평가할 수 있습니다. 실제로 eval
실제로 유효한 용도가 있다면 산술에 있습니다. 다른 모든 항목은 하드 쿼트되어 있습니다. 아무 조치도 취하지 않고 문자열을 연결하고 다른 하드 쿼트 문자열과 마찬가지로 따옴표를 제거합니다.
그러나 두 번째 반환 시(첫 번째 단계에서 따옴표를 제거한 후) 쉘은 다음을 찾습니다.
for i in {2..5}; do echo $i; done
그게했다.
이 문제를 해결하는 다른 방법이 있습니다.
bash -c "for i in {$((1+1))..5}; do echo \$i; done"
^유효합니다.
그리고:
. /dev/fd/0 <<CMD
for i in {$((1+1))..5}; do echo \$i; done
CMD
^유효합니다. 각각의 경우 논리는 동일합니다. 즉, 중괄호 확장이 평가되기 전에 변수가 평가됩니다 bash
.
하지만 저는 스탠드 확장 기능을 많이 사용해 본 적이 없습니다. 나는 보통 다음을 좋아한다:
until [ $((i=$i+1)) -gt 5 ]
do printf %d\\n $i
done