bash'de aritmetik genişlemenin değerlendirilmesi


13

Aşağıdaki satır oluşturur, file_c-6.txtancak çıktı verir 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

Biri kaldırırsa >listeler file_c-6.txtve çıktılar 5:

iİlk örnekte neden değerini korumadığını anlayamıyorum .

$ 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
bu tuhaf.
glenn jackman

2
echoBunun yerine kullanırsam ls, her iki durumda da ikinci şekilde çalışır.
choroba

1
Kod örneğe biraz benzer bu cevap .
Wildcard

4
/bin/echofarkı korur, bu nedenle harici komutlar için çıkış yönlendirmeleri alt kabukta gerçekleşir.
chepner

2
Kesinlikle bug-bash@gnu.org bir hata raporu değer; şu anda geliştirme aşamasında olan 4.4'te düzeltilmemiştir.
chepner

Yanıtlar:


1

Bunu strace altında çalıştırırsanız, kullanılan sürümün lskomutu bir alt kabukta başlattığını görebilirsiniz; burada echo kullanan sürüm, var olan kabuğu içinde çalıştırır.

Çıktısını karşılaştırın

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

karşısında

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

İlkinde göreceksiniz:

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

Ve ikincisinde:

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

Bu son örnekte, cloneyeni bir işlem görüyorsunuz (1258 -> 1259 arası), bu yüzden şimdi bir alt işlemdeyiz. File_c-6.txt dosyasının açılması $((++i)), alt kabukta değerlendirdiğimiz ve stdout'u bu dosyaya ayarlanmış olarak yürütüldüğümüz anlamına gelir ls.

Son olarak, alt sürecin çıktığını görüyoruz, çocuğu topluyoruz, sonra kaldığımız yerden devam ediyoruz ... $i5'e ayarlanmış ve tekrar yankılandığımız şey bu.

(Çocuğun değişikliklerini almak için üst öğede açıkça bir şey yapmadığınız sürece, bir alt işlemdeki değişken değişikliklerin üst işlem için geçerli olmadığını unutmayın)


Mükemmel analiz. Bir çözüm artırılır değeri için geçici bir değişken kullanmak olacaktır: i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i.
Murphy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.