2012-05-14 28 views
10

Tôi đang cố gắng sử dụng bash để mở một bộ mô tả mới để viết các thông báo chẩn đoán thêm. Tôi không muốn sử dụng stderr, bởi vì stderr chỉ nên chứa đầu ra từ các chương trình được gọi là bash. Tôi cũng muốn người mô tả tùy chỉnh của mình có thể được người dùng chuyển hướng.Trong bash, làm thế nào để mở một mô tả tập tin có thể ghi được chuyển hướng bên ngoài?

Tôi cố gắng này:

exec 3>/dev/tty 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 

Nhưng khi tôi cố gắng để chuyển hướng fd 3, sản lượng vẫn ghi vào thiết bị đầu cuối.

$ ./test.sh >/dev/null 2>/dev/null 3>/dev/null 
foo3 
+0

Một trang web hữu ích [link] (http://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/x13082.html) – Mike

+0

Bạn có thể có thể sử dụng một [tên ống] (http: // Linux. die.net/man/1/mkfifo). –

Trả lời

5

đủ đơn giản: Nếu vỏ mẹ là không chuyển hướng fd 3, sau đó test.sh sẽ chuyển hướng fd 3 đến /dev/tty.

if ! { exec 0>&3; } 1>/dev/null 2>&1; then 
    exec 3>/dev/tty 
fi 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 
+0

Nhiều như tôi muốn cung cấp cho gregc chấp nhận, anh ta đã không trở lại trong gần 4 tuần để làm sạch câu trả lời của mình. Điều này, mặc dù có khả năng "mượn" từ gregc, là cách tiếp cận đơn giản nhất. – Kelvin

1

Cập nhật

Điều này có thể được thực hiện. Xem câu trả lời của kaluy cho cách đơn giản nhất.

gốc trả lời

Dường như câu trả lời là "bạn không thể". Bất kỳ bộ mô tả nào được tạo trong tập lệnh đều không áp dụng cho trình bao gọi là tập lệnh.

Tôi đã tìm ra cách sử dụng nó bằng ruby, nếu có ai quan tâm. Xem thêm bản cập nhật sử dụng perl.

begin 
    out = IO.new(3, 'w') 
rescue Errno::EBADF, ArgumentError 
    out = File.open('/dev/tty', 'w') 
end 
p out.fileno 
out.puts "hello world" 

Lưu ý rằng điều này rõ ràng sẽ không hoạt động trong daemon - không được kết nối với thiết bị đầu cuối.

CẬP NHẬT

Nếu ruby ​​không phải là điều của bạn, bạn có thể chỉ cần gọi tập lệnh bash từ tập lệnh ruby. Bạn sẽ cần viên ngọc Open4/thư viện cho đường ống đáng tin cậy của kết quả:

require 'open4' 

# ... insert begin/rescue/end block from above 

Open4.spawn('./out.sh', :out => out) 

UPDATE 2

Đây là một cách sử dụng một chút perl và hầu hết là bash. Bạn phải chắc chắn rằng perl đang hoạt động đúng trên hệ thống của bạn, bởi vì một thực thi perl còn thiếu cũng sẽ trả về một mã thoát khác không.

perl -e 'open(TMPOUT, ">&3") or die' 2>/dev/null 
if [[ $? != 0 ]]; then 
    echo "fd 3 wasn't open" 
    exec 3>/dev/tty 
else 
    echo "fd 3 was open" 
fi 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 
6

Đầu tiên là bộ vỏ mẹ tập tin mô tả từ 3 đến/dev/null
Sau đó, chương trình của bạn đặt tập tin mô tả từ 3 đến/dev/tty
Vì vậy, các triệu chứng của bạn không thực sự đáng ngạc nhiên.

Chỉnh sửa: Bạn có thể kiểm tra xem nếu fd 3 đã được thiết lập:

if [[ ! -e /proc/$$/fd/3 ]] 
then 
    exec 3>/dev/tty 
fi 
+0

+1 để giải thích. Giải pháp bạn đã xóa gần như đã hoạt động. Nó chỉ là '-t' chỉ có thể phát hiện nếu fd được kết nối với một thiết bị đầu cuối, không phải nếu nó tồn tại ở tất cả. – Kelvin

+0

Hãy thử đề xuất mới hơn – cdarke

+1

Tôi chỉ nghĩ về thử nghiệm/proc và sau đó bạn đã đăng nó. Tâm trí tuyệt vời nghĩ như nhau, có lẽ. Thật không may, đây không phải là di động. Hoạt động tốt trên Linux, nhưng không phải mac. Tôi sẽ đưa ra một upvote nếu tôi có thể. – Kelvin

2

Dưới đây là một cách để kiểm tra xem một bộ mô tả tập tin đã được thiết lập bằng cách sử dụng (Bash) chỉ vỏ.

(
# cf. "How to check if file descriptor exists?", 
# http://www.linuxmisc.com/12-unix-shell/b451b17da3906edb.htm 

exec 3<<<hello 

# open file descriptors get inherited by child processes, 
# so we can use a subshell to test for existence of fd 3 
(exec 0>&3) 1>/dev/null 2>&1 && 
    { echo bash: fd exists; fdexists=true; } || 
    { echo bash: fd does NOT exists; fdexists=false; } 

perl -e 'open(TMPOUT, ">&3") or die' 1>/dev/null 2>&1 && 
    echo perl: fd exists || echo perl: fd does NOT exist 

${fdexists} && cat <&3 
) 
+0

+1 Điều này có vẻ đúng. Tôi sẽ kiểm tra một số chi tiết. Nhưng bạn nên tách phần perl, vì có vẻ như đó là một phần của kịch bản. Sau khi đọc kỹ hơn, tôi thấy bạn chỉ sử dụng nó để so sánh. Ngoài ra, tôi không chắc mục đích của 'exec 3 <<< hello' là gì; đặt nó trước khi kiểm tra 'fdexists' sẽ làm cho bài kiểm tra trở thành sự thật mỗi lần. – Kelvin

+0

Tôi đã xác nhận rằng điều này có ý tưởng chính xác nhưng cần dọn dẹp. Nếu bạn có thể lấy kịch bản trong câu hỏi của tôi và sửa đổi nó với giải pháp của bạn, sau đó tôi sẽ chấp nhận câu trả lời của bạn. – Kelvin

+0

phần 'perl' là một giải pháp thay thế. 'exec 3 <<< hello' để kiểm tra. Bỏ ghi chú dòng này có tác dụng tương tự như chuyển hướng mô tả tệp từ bên ngoài. –

1

@Kelvin: Đây là tập lệnh được sửa đổi mà bạn yêu cầu (cộng với một số thử nghiệm).

echo ' 
#!/bin/bash 

# If test.sh is redirecting fd 3 to somewhere, fd 3 gets redirected to /dev/null; 
# otherwise fd 3 gets redirected to /dev/tty. 
#{ exec 0>&3; } 1>/dev/null 2>&1 && exec 3>&- || exec 3>/dev/tty 
{ exec 0>&3; } 1>/dev/null 2>&1 && exec 3>/dev/null || exec 3>/dev/tty 

echo foo1 
echo foo2 >&2 
echo foo3 >&3 

' > test.sh 

chmod +x test.sh 

./test.sh 
./test.sh 1>/dev/null 
./test.sh 2>/dev/null 
./test.sh 3>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 3>&- 
./test.sh 1>/dev/null 2>/dev/null 3>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 3>/dev/tty 

# fd 3 is opened for reading the Here String 'hello' 
# test.sh should see that fd 3 has already been set by the environment 
# man bash | less -Ip 'here string' 
exec 3<<<hello 
cat <&3 
# If fd 3 is not explicitly closed, test.sh will determine fd 3 to be set. 
#exec 3>&- 
./test.sh 

exec 3<<<hello 
./test.sh 3>&- 
+0

Điều này sẽ được đặt tốt nhất trong câu trả lời ban đầu của bạn bằng cách chỉnh sửa nó. –

+0

Ngoài ra, bạn nên sử dụng tài khoản SO ban đầu của mình thay vì tài khoản mới. – Kelvin

+0

Rất tiếc, tính năng này không hoạt động. Nếu tôi chạy tập lệnh, chuyển hướng fd 3 sang tệp, không có gì được ghi vào tệp đó. – Kelvin

Các vấn đề liên quan