Auswertung der arithmetischen Expansion in bash

13

Die folgende Zeile erzeugt file_c-6.txtaber Ausgaben 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

Wenn man es entfernt >, würde es auflisten file_c-6.txtund ausgeben 5:

Ich kann nicht verstehen, warum es iim ersten Beispiel nicht den Wert von behält .

$ 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
Noil Noil
quelle
4
das ist bizarr.
Glenn Jackman
2
Wenn ich echostatt ls, funktioniert es die zweite Möglichkeit , in beiden Fällen.
Choroba
1
Sieht dem Codebeispiel in dieser Antwort etwas ähnlich .
Wildcard
4
/bin/echoDer Unterschied bleibt erhalten, sodass die Ausgabeumleitungen für externe Befehle scheinbar in einer Subshell erfolgen.
Chepner
2
Auf jeden Fall einen Fehlerbericht an [email protected] wert; Es ist nicht in 4.4 behoben, derzeit in der Entwicklung.
Chepner

Antworten:

1

Wenn Sie dies unter Strace ausführen, können Sie feststellen, dass die verwendete Version lsden 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 clonein 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 lsstdout 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.)

unpythonisch
quelle
Hervorragende Analyse. Eine Lösung wäre eine temporäre Variable für den erhöhten Wert zu verwenden: i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i.
Murphy