Làm thế nào tôi có thể thấy dòng lệnh chính xác được thực thi bên trong một số trường hợp bash?


29

Tôi có một bashví dụ chạy dài (bên trong một screenphiên) đang thực thi một tập hợp các lệnh phức tạp bên trong một vòng lặp (với mỗi vòng lặp làm các đường ống, chuyển hướng, v.v.).

Dòng lệnh dài được viết bên trong thiết bị đầu cuối - nó không nằm trong bất kỳ tập lệnh nào. Bây giờ, tôi biết ID tiến trình bash và tôi có quyền truy cập root - làm thế nào tôi có thể thấy dòng lệnh chính xác được thực thi bên trong đó bash?

Thí dụ
bash$ echo $$
1234
bash$ while true ; do \
    someThing | somethingElse 2>/foo/bar | \
    yetAnother ; sleep 600 ; done

Và trong một trường hợp shell khác, tôi muốn xem dòng lệnh được thực thi bên trong PID 1234:

bash$ echo $$
5678
bash$ su -
sh# cd /proc/1234
sh# # Do something here that will display the string  \
   'while true ; do someThing | somethingElse 2>/foo/bar | \
    yetAnother ; sleep 600 ; done'

Điều này có thể không?

EDIT # 1

Thêm ví dụ phản biện cho một số câu trả lời tôi đã có.

  1. Về việc sử dụng bên cmdlinedưới /proc/PID: điều đó không hiệu quả, ít nhất là trong kịch bản của tôi. Đây là một ví dụ đơn giản:

    $ echo $$
    8909
    
    $ while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done
    

    Trong một vỏ khác:

    $ cat /proc/8909/cmdline
    bash
    
  2. Sử dụng ps -p PID --noheaders -o cmdcũng vô dụng:

    $ ps -p 8909 --no-headers -o cmd
    bash
    
  3. ps -eaf cũng không hữu ích:

    $ ps -eaf | grep 8909
    ttsiod    8909  8905  0 10:09 pts/0    00:00:00 bash
    ttsiod   30697  8909  0 10:22 pts/0    00:00:00 sleep 30
    ttsiod   31292 13928  0 10:23 pts/12   00:00:00 grep --color=auto 8909
    

    Đó là, không có đầu ra của dòng lệnh ORIGINAL, đó là thứ tôi đang tìm kiếm - tức là while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

Câu trả lời:


40

Tôi biết tôi đã nắm bắt được ống hút, nhưng UNIX không bao giờ thất bại!

Đây là cách tôi quản lý nó:

bash$ gdb --pid 8909
...
Loaded symbols for /lib/i386-linux-gnu/i686/cmov/libnss_files.so.2
0xb76e7424 in __kernel_vsyscall ()

Sau đó, tại (gdb)dấu nhắc tôi chạy lệnh, call write_history("/tmp/foo")nó sẽ ghi lịch sử này vào tập tin /tmp/foo.

(gdb) call write_history("/tmp/foo")
$1 = 0

Sau đó tôi tách ra khỏi quá trình.

(gdb) detach
Detaching from program: /bin/bash, process 8909

Và bỏ thuốc lá gdb.

(gdb) q

Và chắc chắn ...

bash$ tail -1 /tmp/foo
while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done

Để dễ dàng sử dụng lại trong tương lai, tôi đã viết một tập lệnh bash , tự động hóa quá trình.


1
+1 điều tra rất đẹp. Hãy chắc chắn để đánh dấu điều này là A bị loại trừ trong 2 ngày.
slm

@slm: Cảm ơn! Tôi sẽ viết một bài đăng trên blog về nó - thật thú vị, săn lùng nó.
ttsiodras

1
Với readline kích hoạt bạn cũng có thể làm điều này trong gdb: print (char *)rl_line_buffer. Lệnh hiện tại trong một chuỗi là print (char *)the_printed_command. Bạn cũng có thể call history_builtin(), nhưng điều đó sẽ xuất ra trên tty của quá trình bash, vì vậy nó có thể ít hữu ích hơn.
mr.spuratic

11
@slm: Không thể cưỡng lại - Tôi viết blog về nó ở đây: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras

1
Bash chủ đề có an toàn không? Bạn sẽ phải hy vọng bạn không sửa đổi một số trạng thái nội bộ làm đảo lộn mã hiện đang thực thi khi bạn thực thi một cái gì đó từ gdb. Trong thực tế, bash gần như chắc chắn sẽ chỉ chờ quá trình con của nó kết thúc bất cứ khi nào bạn xảy ra để tạm dừng nó với gdb.
Adrian Pronk

5

Vì lệnh vẫn đang chạy trong màn hình, bash cha của nó không đọc lại bất kỳ lịch sử nào vì vậy:

  • reattach để sàng lọc
  • nhấn ^Zrồiup arrow
  • phần thưởng: gói lệnh trong dấu ngoặc đơn (điều hướng bằng ^A^A- vì màn hình (1) - và ^E) và echo + redirect vào một tệp
  • fg theo đuổi thực thi lệnh

Có những cảnh báo, nhưng điều này là đủ hữu ích, hầu hết thời gian.


Vâng, thậm chí giết nó và nhấn up. Mặc dù bạn phải tự tin rằng nó sẽ bắt đầu lại mà không gặp vấn đề gì, nhưng nếu không thì bạn cũng gặp vấn đề tương tự sau khi khởi động lại. Bạn chỉ cần chọn thời điểm của bạn khi thời gian chết sẽ không thành vấn đề.
Matthieu Napoli

0

Tôi biết bạn đã tìm thấy câu trả lời của riêng mình, nhưng có một lý do nào đó bạn không thể làm điều gì đó như thế này:

(set -x; for f in 1 2 3 4 ; do  echo "$f"; sleep $f; done)

Có lẽ bạn không thể kết hợp đầu ra của công việc thực tế và đầu ra từ bash hiển thị dòng đang thực thi.

Ngoài ra, FWIW, nếu bạn thích tính dài dòng , set -o xtrace.

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.