evaluasi ekspansi aritmatika di bash


13

Baris berikut membuat file_c-6.txttetapi keluaran 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

Jika seseorang menghapusnya >akan daftar file_c-6.txtdan output 5:

Saya tidak mengerti mengapa itu tidak menyimpan nilai idalam contoh pertama.

$ 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
itu aneh.
glenn jackman

2
Jika saya menggunakan echosebagai gantinya ls, itu bekerja dengan cara kedua dalam kedua kasus.
choroba

1
Terlihat agak mirip dengan contoh kode dalam jawaban ini .
Wildcard

4
/bin/echomempertahankan perbedaan, sehingga sepertinya pengalihan output untuk perintah eksternal terjadi dalam subkulit.
chepner

2
Layak laporan bug ke bug-bash@gnu.org; itu tidak diperbaiki di 4.4, saat ini sedang dalam pengembangan.
chepner

Jawaban:


1

Jika Anda menjalankan ini di bawah strace, Anda dapat melihat bahwa versi yang menggunakan lsmemulai perintah dalam sebuah subkulit, di mana versi yang menggunakan gema mengeksekusi semuanya dalam shell yang ada.

Bandingkan keluaran dari

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

melawan

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

Anda akan melihat yang pertama:

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

Dan yang kedua:

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

Dalam contoh terakhir ini, Anda melihat cloneproses baru (dari 1258 -> 1259), jadi sekarang kita sedang dalam proses. Pembukaan file_c-6.txt yang berarti bahwa kami telah mengevaluasi $((++i))dalam subshell, dan eksekusi lsdengan stdout-nya diatur ke file itu.

Akhirnya, kita melihat bahwa subproses keluar, kita menuai anak, lalu kita lanjutkan dengan tempat kita tinggalkan ... dengan $iset ke 5, dan itulah yang kita gema lagi.

(Ingat perubahan variabel dalam suatu subproses tidak meresap ke proses induk, kecuali jika Anda melakukan sesuatu secara eksplisit pada induk untuk mengambil perubahan anak)


Analisis yang sangat baik. Salah satu solusi akan menggunakan variabel sementara untuk nilai bertambah: i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i.
Murphy
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.