Bash 命令的输出是否存储在任何寄存器中?例如类似于 $?
捕获输出而不是退出状态的东西。
我可以将输出分配给一个变量:
output=$(command)
但这是更多的打字......
您可以使用 $(!!)
重新计算(不重复使用)最后一个命令的输出。
!!
自己执行最后一个命令。
$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
答案是不。 Bash 不会将任何输出分配给其内存上的任何参数或任何块。此外,您只能通过其允许的接口操作来访问 Bash。除非您破解 Bash 的私人数据,否则无法访问它。
非常简单的解决方案
一个我用了多年的。
脚本(添加到您的 .bashrc 或 .bash_profile)
# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out; }
# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out; }
用法
$ find . -name 'filename' | cap
/path/to/filename
$ ret
/path/to/filename
我倾向于将 | cap
添加到所有命令的末尾。这样,当我发现我想对运行缓慢的命令的输出进行文本处理时,我总是可以使用 ret
检索它。
cap () { tee /tmp/capture.out; }
和 ret () { cat /tmp/capture.out; }
xci() { xclip -i; xclip -o; }; xco() { xclip -o; };
执行此操作。
一种方法是使用 trap DEBUG
:
f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG
现在最近执行的命令的 stdout 和 stderr 将在 /tmp/out.log
中可用
唯一的缺点是它会执行两次命令:一次将输出和错误重定向到 /tmp/out.log
,一次正常。可能还有一些方法可以防止这种行为。
rm -rf * && cd ..
时,不仅当前目录,而且父目录也会被删除??这种方法对我来说似乎非常危险
trap '' DEBUG
如果您在 Mac 上,并且不介意将输出存储在剪贴板中而不是写入变量,则可以使用 pbcopy 和 pbpaste 作为解决方法。
例如,不要这样做来查找文件并将其内容与另一个文件进行比较:
$ find app -name 'one.php'
/var/bar/app/one.php
$ diff /var/bar/app/one.php /var/bar/two.php
你可以这样做:
$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php
运行第一个命令时,字符串 /var/bar/app/one.php
位于剪贴板中。
顺便说一下,pbcopy
和 pbpaste
中的 pb 代表粘贴板,是剪贴板的同义词。
$ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
pbpaste
与命令替换一起使用。
受到 anubhava 的回答的启发,我认为这实际上是不可接受的,因为它运行每个命令两次。
save_output() {
exec 1>&3
{ [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
exec > >(tee /tmp/current)
}
exec 3>&1
trap save_output DEBUG
这样,最后一个命令的输出位于 /tmp/last 中,并且该命令不会被调用两次。
grep
或 git diff
+1 等实用程序
就像 konsolebox 所说,你必须破解 bash 本身。 Here 是一个很好的例子,说明了如何实现这一目标。 stderred 存储库(实际上是为标准输出着色)提供了有关如何构建它的说明。
我试了一下:在 .bashrc
中定义一些新的文件描述符,例如
exec 41>/tmp/my_console_log
(数字是任意的)并相应地修改 stderred.c
,以便内容也被写入 fd 41。它有点有效,但包含大量 NUL 字节、奇怪的格式并且基本上是二进制数据,不可读.也许对 C 有很好理解的人可以尝试一下。
如果是这样,获取最后打印行所需的一切都是 tail -n 1 [logfile]
。
是的,为什么每次都要输入额外的行;同意。您可以通过管道将命令返回的内容重定向到输入,但是将打印输出重定向到输入 (1>&0) 是不行的,至少对于多行输出来说不行。此外,您不希望在每个文件中一次又一次地编写一个函数。所以让我们试试别的。
一个简单的解决方法是使用 printf 函数将值存储在变量中。
printf -v myoutput "`cmd`"
如
printf -v var "`echo ok;
echo fine;
echo thankyou`"
echo "$var" # don't forget the backquotes and quotes in either command.
另一个可定制的通用解决方案(我自己使用)只运行一次所需的命令并在数组变量中逐行获取命令的多行打印输出。
如果您不将文件导出到任何地方并打算仅在本地使用它,则可以让终端设置函数声明。您必须在 ~/.bashrc
文件或 ~/.profile
文件中添加函数。在第二种情况下,您需要从 Edit>Preferences>yourProfile>Command
启用 Run command as login shell
。
做一个简单的函数,说:
get_prev() # preferably pass the commands in quotes. Single commands might still work without.
{
# option 1: create an executable with the command(s) and run it
#echo $* > /tmp/exe
#bash /tmp/exe > /tmp/out
# option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions.
#echo `"$*"` > /tmp/out
# option 3: (I actually used below)
eval "$*" > /tmp/out # or simply "$*" > /tmp/out
# return the command(s) outputs line by line
IFS=$(echo -en "\n\b")
arr=()
exec 3</tmp/out
while read -u 3 -r line
do
arr+=($line)
echo $line
done
exec 3<&-
}
所以我们在选项 1 中所做的是将整个命令打印到临时文件 /tmp/exe
并运行它并将输出保存到另一个文件 /tmp/out
然后逐行读取 /tmp/out
文件的内容到大批。选项 2 和 3 类似,只是命令按原样执行,而不写入要运行的可执行文件。
在主脚本中:
#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
现在,bash 甚至将变量保存在先前的范围之外,因此即使在主脚本中的函数之外,也可以访问在 get_prev
函数中创建的 arr
变量:
#get previous command outputs in arr
for((i=0; i<${#arr[@]}; i++))
do
echo ${arr[i]}
done
#if you're sure that your output won't have escape sequences you bother about, you may simply print the array
printf "${arr[*]}\n"
编辑:
get_prev()
{
usage()
{
echo "Usage: alphabet [ -h | --help ]
[ -s | --sep SEP ]
[ -v | --var VAR ] \"command\""
}
ARGS=$(getopt -a -n alphabet -o hs:v: --long help,sep:,var: -- "$@")
if [ $? -ne 0 ]; then usage; return 2; fi
eval set -- $ARGS
local var="arr"
IFS=$(echo -en '\n\b')
for arg in $*
do
case $arg in
-h|--help)
usage
echo " -h, --help : opens this help"
echo " -s, --sep : specify the separator, newline by default"
echo " -v, --var : variable name to put result into, arr by default"
echo " command : command to execute. Enclose in quotes if multiple lines or pipelines are used."
shift
return 0
;;
-s|--sep)
shift
IFS=$(echo -en $1)
shift
;;
-v|--var)
shift
var=$1
shift
;;
-|--)
shift
;;
*)
cmd=$option
;;
esac
done
if [ ${#} -eq 0 ]; then usage; return 1; fi
ERROR=$( { eval "$*" > /tmp/out; } 2>&1 )
if [ $ERROR ]; then echo $ERROR; return 1; fi
local a=()
exec 3</tmp/out
while read -u 3 -r line
do
a+=($line)
done
exec 3<&-
eval $var=\(\${a[@]}\)
print_arr $var # comment this to suppress output
}
print()
{
eval echo \${$1[@]}
}
print_arr()
{
eval printf "%s\\\n" "\${$1[@]}"
}
我一直在使用它来打印多个/流水线/两个命令的空格分隔输出作为行分隔:
get_prev -s " " -v myarr "cmd1 | cmd2; cmd3 | cmd4"
例如:
get_prev -s ' ' -v myarr whereis python # or "whereis python"
# can also be achieved (in this case) by
whereis python | tr ' ' '\n'
现在 tr
命令在其他地方也很有用,例如
echo $PATH | tr ':' '\n'
但是对于多个/管道命令......你现在知道了。 :)
-Himanshu
不确定您到底需要什么,所以这个答案可能不相关。您始终可以保存命令的输出:netstat >> output.txt
,但我认为这不是您想要的。
当然也有编程选项;您可以简单地让程序在该命令运行后读取上面的文本文件并将其与变量相关联,在我选择的语言 Ruby 中,您可以使用“反引号”从命令输出中创建一个变量:
output = `ls` #(this is a comment) create variable out of command
if output.include? "Downloads" #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end
将其粘贴在 .rb 文件中并运行它:ruby file.rb
,它将从命令中创建一个变量并允许您对其进行操作。
如果您不想重新计算上一个命令,您可以创建一个宏来扫描当前终端缓冲区,尝试猜测最后一个命令的 -supposed- 输出,将其复制到剪贴板,最后将其键入终端。
它可用于返回单行输出的简单命令(在 Ubuntu 18.04 上使用 gnome-terminal
测试)。
安装以下工具:xdootool
、xclip
、ruby
在 gnome-terminal
中转到 Preferences -> Shortcuts -> Select all
并将其设置为 Ctrl+shift+a
。
创建以下红宝石脚本:
cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}@" }
h = g[0]
print h
EOF
在键盘设置中添加以下键盘快捷键:
bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '
上面的快捷方式:
将当前终端缓冲区复制到剪贴板
提取最后一个命令的输出(只有一行)
将其输入当前终端
我有一个想法,我没有时间尝试立即实施。
但是,如果您执行以下操作怎么办:
$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!
IO 缓冲可能存在问题。此外,文件可能会变得太大。人们必须想出解决这些问题的办法。
我认为使用脚本命令可能会有所帮助。就像是,
script -c bash -qf fifo_pid 解析后使用 bash 特性设置。
仅用于非交互式命令的演示:http://asciinema.org/a/395092
为了还支持交互式命令,您必须从 util-linux 破解 script
二进制文件以忽略任何屏幕重绘控制台代码,并从 bashrc 运行它以将登录会话的输出保存到文件中。
您可以使用 -exec 在命令的输出上运行命令。因此,它将重用输出作为下面使用 find
命令给出的示例:
find . -name anything.out -exec rm {} \;
你在这里说->在当前文件夹中找到一个名为anything.out的文件,如果找到,请将其删除。如果没有找到,-exec 之后的剩余部分将被跳过。
-exec
只是一个 find
特定选项,而不是通用构造。此外,这里没有什么可以在之后重用 find
或 rm
的输出。
$(your_cmd)
:`your_cmd`
根据 Gnu Bash 手册,它们在功能上是相同的。当然,这也受到@memecs 提出的警告的影响git diff $(!!)
的操作时,它肯定很有用,其中上一个命令是find
调用。$()
而不是反引号。但可能没有什么可以失眠的。 github.com/koalaman/shellcheck/wiki/SC2006$()
。$(!!)
) 和执行 (!!
) 有什么区别?