readline 可能不是問題,但沒有它我無法重現這種行為
這是一個 shell 的極簡復制,它在 STDOUT 上列印一個提示,如果命令以“echo”開頭,它會列印接下來的內容:
mybash.c :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
int main(void)
{
char *prompt;
char *line_input;
prompt = "[mybash]> ";
while (1)
{
line_input = readline(prompt);
if (line_input)
{
if (!strncmp(line_input, "echo ", 5))
{
write(1, line_input 5, strlen(line_input) - 5);
write(1, "\n", 1);
}
free(line_input);
}
if (!line_input)
{
write(2, "exit\n", 5);
exit(0);
}
}
return (0);
}
起初看起來它作業正常:
退出是因為我按了 Ctrl-D,或者在后面的例子中因為管道結束
[bash]$ gcc mybash.c -lreadline
[bash]$ ./a.out
[mybash]> echo hello
hello
[mybash]> anything
[mybash]> exit
[bash]$
現在我把它放在一個管道中來捕獲輸出
[bash]$ echo "echo hello" | ./a.out &>file.log
[bash]$ cat file.log
[mybash]> echo hello
hello
[mybash]> exit
[bash]$
(為了便于閱讀,我在 cat 輸出的行首添加了空格)
好的,現在我將提示更改為很長的內容:
prompt = WHITE"[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> "RESET;
并且輸出在經典互動中運行良好,它在僅重定向 STDERR 或 STDOUT 的管道中也運行良好,但如果我重定向兩者,它會中斷:
互動式提示:
[bash]$ ./a.out
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> echo hello
hello
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> anything
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> exit
[bash]$
沒有重定向的管道:
[bash]$ echo "echo hello" | ./a.out
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> echo hello
hello
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> anything
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> exit
[bash]$
如果不清楚,我只寫了第一個命令echo "echo hello" | ./a.out,其余的提示“自動”,與前面的示例不同
標準輸出重定向:
[bash]$ echo "echo hello" | ./a.out 1>file.log
exit
[bash]$ cat file.log
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> echo hello
hello
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> [%]
[bash]$
STDERR 重定向:
[bash]$ echo "echo hello" | ./a.out 2>file.log
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> echo hello
hello
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> [%]
[bash]$ cat file.log
exit
[bash]$
互動模式下的 STDOUT 和 STDERR 重定向:
[bash]$ ./a.out &>file.log
(i type here without seeing anything)
[bash]$ cat file.log
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> echo hello
hello
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> anything
[mybash_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]> exit
[bash]$
和管道中的 STDOUT 和 STDERR 重定向:
[bash]$ echo "echo hello" | ./a.out &>file.log
[bash]$ cat file.log
_file]>echo hellog_prompt_to_test_the_output_in_case_of_a_redirection_in_a
hello
exith_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]>
[bash]$
抱歉,在這個例子中,stackoverflow 的顏色有點中斷
正如你所看到的,在最后一個例子中,提示行沒有轉到新行,它在開始時自己寫,但它只發生在&>重定向和管道上,我不明白為什么?
當然,同樣的測驗bash而不是mybash效果很好,提示不會中斷
uj5u.com熱心網友回復:
這似乎是一個錯誤。我可以用 libreadline v7 重現它,但不能用 v8 重現它。使用 v7 我得到:
cat file.log
]> echo helloy_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]
hello
]> exit_a_very_long_prompt_to_test_the_output_in_case_of_a_redirection_in_a_file]
您可以通過strace以下方式運行命令:
... strace -x -f -o strace1.out -s 2048 ./a.out ...
并進行比較。您會注意到,如果至少有一個輸入或輸出是真正的終端,您將得到:
ioctl(2, TCGETS, ... = 0
ioctl(0, TCGETS, ... = -1 ENOTTY (Inappropriate ioctl for device)
或者
ioctl(2, TCGETS, ... = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(0, TCGETS, ... = 0
進而:
write(1, "[..._in_a_file]> ", 83) = 83
但是當輸入和輸出都不是真正的終端時,例如輸入是一個管道,輸出被重定向到一個檔案,你會得到:
ioctl(2, TCGETS, ... = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(0, TCGETS, ... = -1 ENOTTY (Inappropriate ioctl for device)
...
write(1, "[..._in_a_file]\r]> ", 85) = 85
注意到了\r]>嗎?\r覆寫的原因。
uj5u.com熱心網友回復:
語法錯誤,AFAIK。
使用echo "echo hello" | ./a.out 1>file.log 2>&1.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/373052.html
