배쉬에서의 산술 확장 평가


13

다음 행은 작성 file_c-6.txt하지만 출력합니다 5.

$ i=5; ls file_a-${i}.txt file_b-${i}.txt > file_c-$(( ++i )).txt; echo $i
5
$ cat file_c-6.txt
file_a-5.txt
file_b-5.txt

하나를 제거 >하면 나열 file_c-6.txt되고 출력됩니다 5.

i첫 번째 예제에서 왜 가치를 유지하지 못하는지 이해할 수 없습니다 .

$ i=5; ls file_a-${i}.txt file_b-${i}.txt file_c-$(( ++i )).txt; echo $i
file_a-5.txt  file_b-5.txt  file_c-6.txt
6

4
기괴하다.
glenn jackman

2
echo대신에 사용 ls하면 두 경우 모두 두 번째 방식으로 작동합니다.
choroba

1
이 답변 의 코드 예제와 다소 비슷합니다 .
와일드 카드

4
/bin/echo차이점을 유지하므로 외부 명령의 출력 리디렉션이 하위 셸에서 발생하는 것처럼 보입니다.
chepner

2
bug-bash@gnu.org에 버그보고를 할 가치가 있습니다. 현재 개발중인 4.4에서는 수정되지 않았습니다.
chepner

답변:


1

strace에서 이것을 실행하면, 사용하는 버전이 ls서브 쉘에서 명령을 시작 한다는 것을 알 수 있습니다. echo를 사용하는 버전은 기존 쉘에서 모두 실행합니다.

의 출력을 비교

$ strace -f /bin/bash -o trace.txt -c 'i=5; echo $i; echo file_c-$((++i)).txt; echo $i'
5
6
6

에 맞서

strace -f /bin/bash -o trace.txt -c 'i=5; echo $i; ls > file_c-$((++i)).txt; echo $i'
5
5

첫 번째에서 볼 수 있습니다 :

1251  execve("/bin/bash", ["/bin/bash", "-c", "i=5; echo $i; echo file_c-$(( ++"...], [/* 19 vars */]) = 0
...
1251  write(1, "5\n", 2)                = 2
1251  write(1, "file_c-6.txt\n", 13)    = 13
1251  write(1, "6\n", 2)                = 2

그리고 두 번째로 :

1258  execve("/bin/bash", ["/bin/bash", "-c", "i=5; echo $i; ls > file_c-$(( ++"...], [/* 19 vars */]) = 0
...
1258  write(1, "5\n", 2)                = 2
...
1258  stat("/bin/ls", {st_mode=S_IFREG|0755, st_size=110080, ...}) = 0
1258  access("/bin/ls", R_OK)           = 0
1258  clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7301f40a10) = 1259
1259  open("file_c-6.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
1259  dup2(3, 1)                        = 1
1259  close(3)                          = 0
1259  execve("/bin/ls", ["ls"], [/* 19 vars */]) = 0
1259  write(1, "71\nbin\nfile_a-5.txt\nfile_b-5.txt"..., 110) = 110
1259  close(1)                          = 0
1259  munmap(0x7f0e81c56000, 4096)      = 0
1259  close(2)                          = 0
1259  exit_group(0)                     = ?
1259  +++ exited with 0 +++
1258  <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1259
1258  rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f7301570d40}, {0x4438a0, [], SA_RESTORER, 0x7f7301570d40}, 8) = 0
1258  rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
1258  --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1259, si_status=0, si_utime=0, si_stime=0} ---
1258  wait4(-1, 0x7ffd23d86e98, WNOHANG, NULL) = -1 ECHILD (No child processes)
1258  rt_sigreturn()                    = 0
1258  write(1, "5\n", 2)                = 2

이 마지막 예 clone에서 1258-> 1259의 새 프로세스 를 확인 했으므로 이제 하위 프로세스에 있습니다. file_c-6.txt의 시작은 하위 $((++i))쉘에서 평가 ls했으며 stdout을 해당 파일로 설정 하여 실행 한 것을 의미합니다.

마지막으로, 우리는 하위 프로세스가 종료되고 자식을 거둔 다음 중단 된 부분으로 계속 진행하고 $i5 로 설정 한 것을 다시 보게됩니다.

(하위 프로세스에서 변수 변경 사항을 기억하면 하위 변경 사항을 파악하기 위해 부모에서 명시 적으로 무언가를 수행하지 않는 한 상위 프로세스까지 포함되지 않습니다)


뛰어난 분석. 한 가지 해결책은 증가 된 값에 임시 변수를 사용하는 것 i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i입니다.
머피
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.