การประเมินผลของการขยายตัวทางคณิตศาสตร์ในทุบตี


13

บรรทัดต่อไปนี้สร้างfile_c-6.txtแต่เอาต์พุต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

หากลบออก>จะเป็นการแสดงรายการfile_c-6.txtและเอาท์พุท5:

ฉันไม่สามารถเข้าใจได้ว่าทำไมมันไม่เก็บค่าของiในตัวอย่างแรก

$ 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
นั่นแปลกประหลาด
เกล็

2
ถ้าฉันใช้echoแทนlsมันจะใช้วิธีที่สองในทั้งสองกรณี
choroba

1
ดูคล้ายกับตัวอย่างรหัสในคำตอบนี้
สัญลักษณ์แทน

4
/bin/echoรักษาความแตกต่างดังนั้นจึงดูเหมือนว่าการเปลี่ยนเส้นทางเอาต์พุตสำหรับคำสั่งภายนอกเกิดขึ้นใน subshell
chepner

2
คุ้มค่ารายงานข้อผิดพลาดไปยัง bug-bash@gnu.org; ยังไม่ได้แก้ไขใน 4.4 ปัจจุบันอยู่ระหว่างการพัฒนา
chepner

คำตอบ:


1

หากคุณเรียกใช้สิ่งนี้ภายใต้ strace คุณจะเห็นว่าเวอร์ชันที่ใช้lsเริ่มต้นคำสั่งในเชลล์ย่อยซึ่งเวอร์ชันที่ใช้ echo เรียกใช้งานมันทั้งหมดในเชลล์ที่มีอยู่

เปรียบเทียบผลลัพธ์ของ

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

ต่อต้าน

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

คุณจะเห็นในครั้งแรก:

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

และในวินาที:

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

ในตัวอย่างสุดท้ายนี้คุณเห็นcloneกระบวนการใหม่ (จาก 1258 -> 1259) ดังนั้นตอนนี้เราอยู่ในกระบวนการย่อย การเปิด file_c-6.txt ซึ่งหมายความว่าเราได้ประเมินผล$((++i))ใน subshell และการดำเนินการของlsstdout ที่ตั้งไว้ในไฟล์นั้น

ในที่สุดเราจะเห็นว่ากระบวนการย่อยจบลงแล้วเราจะเก็บเด็กอีกครั้งจากนั้นเราดำเนินการต่อจากจุดที่เราค้างไว้ ... ด้วยการ$iตั้งค่าเป็น 5 และนั่นคือสิ่งที่เราสะท้อนออกมาอีกครั้ง

(โปรดจำไว้ว่าการเปลี่ยนแปลงของตัวแปรในกระบวนการย่อยไม่ได้ขึ้นอยู่กับกระบวนการหลักเว้นแต่คุณจะทำอะไรบางอย่างอย่างชัดเจนในพาเรนต์เพื่อรับการเปลี่ยนแปลงของเด็ก)


การวิเคราะห์ที่ยอดเยี่ยม i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $iทางออกหนึ่งที่จะใช้ตัวแปรชั่วคราวสำหรับค่าเพิ่มขึ้นนี้
เมอร์ฟี
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.