Wenn Sie dies unter Strace ausführen, können Sie feststellen, dass die verwendete Version ls
den Befehl in einer Subshell startet, in der die Version, die Echo verwendet, alles in der vorhandenen Shell ausführt.
Vergleichen Sie die Ausgabe von
$ strace -f /bin/bash -o trace.txt -c 'i=5; echo $i; echo file_c-$((++i)).txt; echo $i'
5
6
6
gegen
strace -f /bin/bash -o trace.txt -c 'i=5; echo $i; ls > file_c-$((++i)).txt; echo $i'
5
5
Sie werden in der ersten sehen:
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
Und im zweiten:
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
In diesem letzten Beispiel sehen Sie das clone
in einem neuen Prozess (von 1258 -> 1259). Jetzt befinden wir uns in einem Unterprozess. Das Öffnen von file_c-6.txt bedeutet, dass wir ausgewertet haben$((++i))
in der Subshell haben, und das Ausführen von ls
stdout mit dieser Datei.
Schließlich sehen wir, dass der Unterprozess beendet wird, wir ernten das Kind, dann fahren wir dort fort, wo wir aufgehört haben ... mit $i
Einstellung 5, und das ist, was wir wieder ausgeben.
(Denken Sie daran, dass Variablenänderungen in einem Unterprozess nicht bis zum übergeordneten Prozess durchdringen, es sei denn, Sie führen im übergeordneten Prozess eine explizite Aktion aus, um die Änderungen des untergeordneten Prozesses zu erfassen.)