所以我希望我运行的所有命令都通过管道传输到另一个命令,并且我尝试使用 .bashrc 函数:
* () {
${@} | [my command]
}
Run Code Online (Sandbox Code Playgroud)
例如,我真正想要实现的是一种将上次运行的命令的标准输出存储在临时文件中的方法,您可以使用另一个命令调用该文件:
* () {
${@} | tee /tmp/last_command
}
Run Code Online (Sandbox Code Playgroud)
解释我想要实现的目标的另一种方式:类似 ALIAS [any_command]='[any_command] | tee /tmp/last_command'
所以这是我将通过tee示例实现的实际示例:
$ tail -5 /etc/passwd
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
Run Code Online (Sandbox Code Playgroud)
命令运行时实际发生了什么(但我不必每次都写它):
$ tail -5 /etc/passwd | tee /tmp/last_command
Run Code Online (Sandbox Code Playgroud)
所以这是一个完整的例子:
$ tail -5 /etc/passwd
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
$ cat /tmp/last_command
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
$ date
Tue Jan 14 13:26:04 EET 2020
$ cat /etc/last_command
Tue Jan 14 13:26:04 EET 2020
Run Code Online (Sandbox Code Playgroud)
Bash 不太灵活,您可以在每个命令行之前和之后运行命令/函数 - 但仅在 bash 中捕获输出并不简单。
一些问题包括:
ioctl()),许多程序都会检测到这一点(例如,甚至)。您不会想将其与交互式编辑器一起使用,例如.fseek()fstat()manlsvitime诸如“ ”、管道甚至 shell 循环之类的特殊命令可能会使解决方案变得复杂,并且管道会改变返回代码alias仅在词法上替换命令stdout并且stderr可能会遇到交错问题.” ( source) 也exit同样受到影响。(在下面使用 readline 的选项中,Ctrl-j可用于运行此类命令。)这(大致)是我用于类似问题、称为 的函数等的方法,因此我可以仅在我想要捕获/处理其输出的命令(例如 X 选择或剪贴板)中添加前缀+:++
function +() (
set -f # no double expand
eval "$@" | tee >( tr -s "\n" " " | xclip )
return ${PIPESTATUS[0]} # real rc
)
Run Code Online (Sandbox Code Playgroud)
例如,+ find /tmp -name "*.pdf" -mtime -30
它不完美,但基本上满足了我的需要。
上面,@mosvy 建议script,这会创建一个新的 tty(终端),在其中运行一个程序,从而回避所有这些问题。它将所有输出写入父 tty,同时也将其记录到文件中。它记录整个会话或单个命令,因此您仍然面临如何自动调用它的问题。
相反,使用screen(因为与script它不同的是可以与正在运行的实例交互并控制它)和 bash 的PROMPT_COMMAND:
if [[ "$TERM" -eq "screen" && -n "$STY" ]]; then
PROMPT_COMMAND=screenlog
SCREENLOG="${HOME}/screenlog"
fi
function screenlog() {
[[ -n "$STY" ]] && {
screen -S "$STY" -X log off
[[ -f "${SCREENLOG:=$HOME/screenlog}" ]] && mv "$SCREENLOG" /tmp/last_command
screen -S "$STY" -X logfile "$SCREENLOG"
screen -S "$STY" -X log on
}
return 0
}
Run Code Online (Sandbox Code Playgroud)
如果您符合上述要求.bashrc,那么您应该可以开始了screen。每次 bash 显示新的命令提示符时都会调用上述函数。它首先检查它是否在屏幕下运行,然后与屏幕对话以发出停止日志记录的命令,将最后的输出写入/tmp/last_command,然后告诉屏幕开始日志记录。
需要注意的是,由于PROMPT_COMMANDs 是在显示提示之前调用的,因此提示和命令(包括诸如退格键之类的编辑)都将出现在日志文件的开头。解决这个问题的一种方法是使用 DEBUG 陷阱模拟“pre-exec”来运行上面的命令,例如: 在执行之前通过程序修改所有 bash 命令。如果您运行“交互式”或终端感知程序(例如man或top),您也会记录终端控制序列,如果您想播放输出,这很有用(尽管script/scriptreplay在这方面更好)。
在底层,bash 使用 readline,这允许将键绑定到 readline 函数、其他键的序列或 shell 函数(但不是这三种类型的组合)。这相对容易做到:
bind '"\C-e": end-of-line'
bind '"\C-j": accept-line'
bind '"\C-m": "\C-e | tee /tmp/last_command\C-j"'
Run Code Online (Sandbox Code Playgroud)
前两行只是偏执地确保Ctrl-E和Ctrl-J按预期绑定(如果你有一个没有accept-line绑定的终端,你就不再有终端了......)。最后一行将通常绑定到 , 的Ctrl- (又名“Return”)替换为以下序列:Maccept-line
| tee /tmp/last_command(文字)当发出的命令本身发生更改时,这在屏幕和历史记录中可见。缺点是这种简单的方法没有“自我意识”,并且盲目地改变每一个命令,甚至是历史上已经应用了更改的命令。
这是一个更复杂的版本,它使用一个函数在发送命令时修改命令:
bind '"\C-e": end-of-line'
bind '"\C-j": accept-line'
bind -x '"\e\C-a": _recorder'
function _record() {
tee /tmp/this_command
[[ -f /tmp/this_command ]] && mv /tmp/this_command /tmp/last_command
}
function _recorder() {
local text="| _record"
[[ -z "${READLINE_LINE}" ]] && return 0
[[ "$READLINE_LINE" =~ "${text}"$ ]] || READLINE_LINE+=" $text"
return 0
}
bind '"\C-m": "\C-e\e\C-a\C-j"'
Run Code Online (Sandbox Code Playgroud)
步骤 2 被 bash 函数替换_recorder,绑定到Esc, Ctrl- a(您不需要键入,它只需要绑定到击键,因为宏只能包含击键)。
这个函数可以是任意智能的,这里它处理输出文件竞争,并在更改输入行之前检查管道是否已经存在。如果存在其他重定向,您还可以将整个命令行包装在子 shell 中(尽管您很快就会发现在 bash 中解析 bash 命令行很复杂)。
您可以通过即时修复历史记录来进一步(!)复合黑客行为:
function myprompt() {
local _seq _cmdline
local text="| _record"
read _seq _cmdline < <(HISTTIMEFORMAT= history 1) # previous command
[[ "${_cmdline}" =~ (.*)" ${text}"$ ]] && {
_cmdline="${BASH_REMATCH[1]}"
history -d $_seq # delete entry
history -s "$_cmdline" # restore original
}
}
PROMPT_COMMAND=myprompt
Run Code Online (Sandbox Code Playgroud)
readline 方法还有一个重要的缺陷:通常可以在多行上输入的非简单命令会被这种方法破坏,例如 ( while, for)。
| 归档时间: |
|
| 查看次数: |
730 次 |
| 最近记录: |