Chương trình Bash không được thực thi nếu chuyển hướng sẽ thất bại


9

Trong bash, tôi nhận thấy rằng nếu một lệnh sử dụng chuyển hướng sẽ thất bại, bất kỳ chương trình nào chạy trước đó sẽ không chạy.

Ví dụ, chương trình này mở tệp "a" và ghi 50 byte vào tệp "a". Tuy nhiên, chạy lệnh này với chuyển hướng đến một tệp không đủ quyền (~ root / log), sẽ không mang lại thay đổi nào về kích thước tệp của "a".

$ ./write_file.py >> ~root/log
-bash: /var/root/log: Permission denied
cdal at Mac in ~/experimental/unix_write
$ ls -lt
total 16
-rw-rw-r--  1 cdal  staff  0 Apr 27 08:54 a <-- SHOULD BE 50 BYTES

Mọi người sẽ nghĩ chương trình sẽ chạy, nắm bắt bất kỳ đầu ra nào (nhưng cũng ghi vào tệp "a"), và sau đó không thể ghi bất kỳ đầu ra nào vào ~ root / log. Thay vào đó chương trình không bao giờ chạy.

Tại sao lại như vậy và làm thế nào để bash chọn thứ tự "kiểm tra" mà nó thực hiện trước khi thực hiện một chương trình? Các kiểm tra khác được thực hiện là tốt?

ps Tôi đang cố xác định xem một chương trình chạy dưới cron có thực sự chạy khi được chuyển hướng đến tệp "bị từ chối cấp phép" hay không.


Mọi thứ đều hoạt động tốt (nghĩa là quyền sở hữu và quyền của tệp .py của bạn), chương trình của bạn thực thi tốt. Vấn đề của bạn đến từ chuyển hướng. Bạn không có quyền để viết một thư mục filein / root. Và bạn đã chuyển hướng của bạn stdoutđể làm chính xác điều đó. Vì vậy, bạn sẽ không thấy bất kỳ đầu ra nào, mặc dù chương trình của bạn đã chạy.
MelBurslan

2
Mel, điều đó không đúng, chương trình không bao giờ thực sự chạy. Xem câu trả lời dưới đây.
Charlie Dalsass

Bạn: "Chạy write_file.pychương trình và gửi đầu ra của nó tới ~root/logbash:" Xin lỗi, nhưng bạn không được phép ghi vào tệp đó! "Shell đang làm chính xác những gì nó nên làm. Nếu nó không thể làm những gì bạn yêu cầu làm, nó ngay lập tức thông báo cho bạn tại sao có vấn đề, cho bạn cơ hội quyết định cách xử lý. Nếu nó đủ quan trọng, bạn đã chỉ định một nơi để lưu nó, thì sẽ là sai đối với ASS | U | ME, bạn có thể chạy mà không lưu stdout.
Monty Harder

Câu trả lời:


18

Đây thực sự không phải là một câu hỏi về việc kiểm tra thứ tự, chỉ đơn giản là thứ tự mà vỏ thiết lập mọi thứ. Chuyển hướng được thiết lập trước khi lệnh được chạy; vì vậy trong ví dụ của bạn, shell cố gắng mở ~root/logđể nối thêm trước khi thử làm bất cứ điều gì liên quan ./write_file.py. Vì tệp nhật ký không thể mở được, chuyển hướng thất bại và shell dừng xử lý dòng lệnh tại thời điểm đó.

Một cách để chứng minh điều này là lấy một tệp không thể thực thi và cố gắng chạy nó:

$ touch demo
$ ./demo
zsh: permission denied: ./demo
$ ./demo > ~root/log
zsh: permission denied: /root/log

Điều này cho thấy lớp vỏ thậm chí không nhìn vào ./demokhi chuyển hướng không thể được thiết lập.


Wow, nó đơn giản phải không? Tôi đã không nhận ra rằng chuyển hướng đã được thực hiện đầu tiên. Cảm ơn câu trả lời này và cho câu trả lời tuyệt vời khác là tốt.
Charlie Dalsass

6
Nếu họ không hoàn thành trước, đầu ra sẽ được ghi vào đâu?
Charles Duffy

Và nếu đầu ra không thể được viết, làm sao chúng ta biết chạy an toàn? Có thể lệnh xuất thông tin đang bị xóa khỏi kho lưu trữ dữ liệu và điều hoàn toàn cần thiết là đầu ra phải được ghi lại. Điều tốt là bash sẽ không cho phép nó chạy cho đến khi bạn sửa các quyền đó, eh?
Monty Harder

11

Từ trang bash man, phần GIẢM GIÁ (nhấn mạnh bởi tôi):

Trước khi một lệnh được thực thi, đầu vào và đầu ra của nó có thể được chuyển hướng bằng cách sử dụng một ký hiệu đặc biệt được giải thích bởi shell.

...

Không thể mở hoặc tạo tệp khiến chuyển hướng không thành công.

Vì vậy, shell cố gắng mở tệp đích stdout, không thành công và lệnh không được thực thi.


Cám ơn rất nhiều. Tôi muốn trang man làm rõ một chút với "... nếu đầu ra không thể được chuyển hướng, chương trình sẽ không được thực thi".
Charlie Dalsass

Cập nhật; nó khá ẩn một số đoạn dưới đây.
Murphy

Trên thực tế, nó khá rõ ràng. "Không thể mở hoặc tạo tệp khiến chuyển hướng không thành công." Nó đây rồi Cảm ơn một lần nữa.
Charlie Dalsass

3

Thật đáng để quan sát rằng vỏ phải thiết lập chuyển hướng trước khi bắt đầu chương trình.

Hãy xem xét ví dụ của bạn:

./write_file.py >> ~root/log

Những gì xảy ra trong vỏ là:

  1. Chúng tôi (vỏ) fork(); tiến trình con kế thừa các mô tả tệp mở từ cha mẹ của nó (shell).
  2. Trong tiến trình con, chúng tôi fopen()(mở rộng) "~ root / log" và dup2()nó thành fd 1 (và close()fd tạm thời). Nếu fopen()thất bại, hãy gọi exit()để báo cáo lỗi cho phụ huynh.
  3. Vẫn còn trong đứa trẻ, chúng tôi exec()"./write_file.py". Quá trình này hiện không còn chạy bất kỳ mã nào của chúng tôi (trừ khi chúng tôi không thực thi, trong trường hợp đó chúng tôi exit()sẽ báo cáo lỗi cho phụ huynh).
  4. Phụ huynh sẽ wait()cho trẻ chấm dứt và xử lý mã thoát của nó ( $?ít nhất là sao chép nó vào ).

Vì vậy, sự chuyển hướng phải xảy ra ở trẻ giữa fork()exec(): nó không thể xảy ra trước đó fork()vì nó không phải thay đổi thiết bị xuất chuẩn của shell và nó không thể xảy ra sau exec()vì tên tệp và mã thực thi của shell đã được thay thế bởi chương trình Python . Phụ huynh không có quyền truy cập vào các mô tả tập tin của trẻ (và ngay cả khi có, nó không thể đảm bảo chuyển hướng giữa exec()và ghi đầu tiên vào thiết bị xuất chuẩn).


0

Tôi rất tiếc phải thông báo cho bạn rằng nó hoàn toàn ngược lại. Shell cần mở I / O trước và sau đó chuyển điều khiển cho chương trình.

tee có thể chứng minh hữu ích trong trường hợp này: ./write_file.py | tee -a ~root/log > /dev/null


Tập lệnh Python chỉ chết trên SIGPIPE sau khi tee thất bại?
Kevin

Không theo thử nghiệm tôi đã làm nhưng tôi khuyên bạn nên thử nó.
Julie Pelletier
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.