đánh giá mở rộng số học trong bash


13

Dòng sau tạo file_c-6.txtnhưng đầu ra 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

Nếu ai xóa >nó sẽ liệt kê file_c-6.txtvà xuất ra 5:

Tôi không thể hiểu tại sao nó không giữ giá trị itrong ví dụ đầu tiên.

$ 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
thật kỳ quái
glenn jackman

2
Nếu tôi sử dụng echothay vì ls, nó hoạt động theo cách thứ hai trong cả hai trường hợp.
choroba

1
Trông hơi giống với ví dụ mã trong câu trả lời này .
tự đại diện

4
/bin/echoduy trì sự khác biệt, vì vậy có vẻ như các chuyển hướng đầu ra cho các lệnh bên ngoài xảy ra trong một lớp con.
chepner

2
Chắc chắn có giá trị một báo cáo lỗi đến bug-bash@gnu.org; nó không được sửa trong 4.4, hiện đang được phát triển.
chepner

Câu trả lời:


1

Nếu bạn chạy nó dưới strace, bạn có thể thấy rằng phiên bản sử dụng lskhởi động lệnh trong một lớp con, trong đó phiên bản sử dụng echo thực thi tất cả trong shell hiện có.

So sánh đầu ra của

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

chống lại

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

Bạn sẽ thấy trong phần đầu tiên:

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

Và trong lần thứ hai:

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

Trong ví dụ cuối cùng này, bạn thấy clonemột quy trình mới (từ 1258 -> 1259), vì vậy bây giờ chúng tôi đang ở trong một quy trình con. Việc mở file_c-6.txt có nghĩa là chúng tôi đã đánh giá $((++i))trong lớp con và việc thực hiện lsvới thiết bị xuất chuẩn của nó được đặt thành tệp đó.

Cuối cùng, chúng ta thấy rằng quy trình con thoát ra, chúng ta gặt đứa trẻ, sau đó chúng ta tiếp tục với nơi chúng ta rời đi ... với $ithiết lập là 5, và đó là những gì chúng ta lặp lại.

(Hãy nhớ các thay đổi biến trong quy trình con không phù hợp với quy trình cha, trừ khi bạn làm điều gì đó rõ ràng ở cha mẹ để lấy các thay đổi của con)


Phân tích xuất sắc. Một giải pháp sẽ là sử dụng một biến tạm thời cho giá trị tăng dần : i=5; j=$(( i + 1 )); ls file_a-${i}.txt file_b-${i}.txt > file_c-${j}.txt; i=${j}; echo $i.
Murphy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.