valutazione dell'espansione aritmetica in bash


13

La seguente riga crea file_c-6.txtma genera 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

Se uno viene rimosso, >viene elencato file_c-6.txte prodotto 5:

Non riesco a capire perché non mantenga il valore di inel primo esempio.

$ 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
è bizzarro.
Glenn Jackman,

2
Se uso echoinvece di ls, funziona in entrambi i casi nel secondo modo.
Choroba,

1
Sembra in qualche modo simile all'esempio di codice in questa risposta .
Wildcard il

4
/bin/echoconserva la differenza, quindi sembra che i reindirizzamenti dell'output per i comandi esterni avvengano in una subshell.
Chepner,

2
Sicuramente merita una segnalazione di bug a bug-bash@gnu.org; non è stato risolto in 4.4, attualmente in fase di sviluppo.
Chepner,

Risposte:


1

Se lo esegui in strace, puoi vedere che la versione che usa lsavvia il comando in una subshell, dove la versione che usa echo esegue tutto nella shell esistente.

Confronta l'output di

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

contro

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

Vedrai nel primo:

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

E nel secondo:

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 questo ultimo esempio, si vede clonein un nuovo processo (dal 1258 -> 1259), quindi ora siamo in un sottoprocesso. L'apertura di file_c-6.txt che significa che abbiamo valutato $((++i))nella subshell e l'esecuzione di lscon il suo stdout impostato su quel file.

Infine, vediamo che il sottoprocesso esce, raccogliamo il bambino, quindi continuiamo da dove avevamo interrotto ... con $iset su 5, ed è quello che riecheggiamo di nuovo.

(Ricorda che le modifiche variabili in un sottoprocesso non percorrono fino al processo genitore, a meno che tu non faccia esplicitamente qualcosa nel genitore per afferrare le modifiche del figlio)


Eccellente analisi. Una soluzione potrebbe essere quella di utilizzare una variabile temporanea per il valore incrementato: i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i.
Murphy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.