![正在运行的脚本可以识别日志记录上下文吗?](https://linux22.com/image/52702/%E6%AD%A3%E5%9C%A8%E8%BF%90%E8%A1%8C%E7%9A%84%E8%84%9A%E6%9C%AC%E5%8F%AF%E4%BB%A5%E8%AF%86%E5%88%AB%E6%97%A5%E5%BF%97%E8%AE%B0%E5%BD%95%E4%B8%8A%E4%B8%8B%E6%96%87%E5%90%97%EF%BC%9F.png)
假设我将程序的 重定向到文件STDOUT
。STDERR
./script.sh 1> output.log 2> error.log
正在运行的程序能否发现这一点,即知道这些文件的路径吗?
答案1
{ readlink /dev/fd/[1,2] ; echo "out" ; } >./file 2>./error
{ readlink /dev/fd/0 ; cat ; } <./file
输出:
/home/mikeserv/file
/home/mikeserv/file
/home/mikeserv/error
out
{ readlink /proc/$$/fd/[1,2] ; echo out ; } >./file 2>./error
{ readlink /proc/$$/fd/0 ; cat ; } <./file
输出:
/home/mikeserv/file
/home/mikeserv/file
/home/mikeserv/error
out
答案2
您可以致电lsof
列出 shell 进程打开的文件。用于-a -p $$
将输出限制为 shell 进程 ( $$
),-d 1
将输出限制为文件描述符 1(例如),并-F n
以可解析的形式打印输出。下面是一个处理文件名中任意字符的 shell 片段:
output_file=$(lsof -a -p $$ -d 1 -F pn; echo .)
output_file=${output_file%.}
output_file=${output_file#n}
如果文件名不包含换行符,您将能够摆脱output_file=$(lsof -a -p $$ -d 1 -F pn | sed -n '2s/.//p')
.
请注意,文件名可能并不总是存在,特别是在文件已被删除的情况下。
在 Linux 下,访问文件名的另一种方法是通过/proc/$$/fd
:/proc/$$/fd/1
是一个稍微神奇的符号链接,指向 shell 在文件描述符 1 上打开的文件(即使返回的文件名readlink
不存在,该链接也可以工作,例如在已删除文件的情况)。
利用通过这种方式获得的信息通常是一个非常糟糕的主意。如果有人调用您的脚本并将输出重定向到文件,如果您因文件位置而表现不同,或者以附加到文件以外的方式影响文件,他们将不会喜欢它。有一个例外:您可能希望根据是否写入终端或其他内容(管道、套接字、文件)做出不同的反应,例如在终端上显示颜色或进度指示器。有一个特定的测试来确定文件描述符是否连接到终端:
if [ -t 2 ]; then
# stderr is a terminal
printf 1>&2 '\e[31mError: widget not found\e[0m'
else
# stderr is not a terminal
echo 1>&2 'Error: widget not found'
fi
答案3
在您的情况下,STDOUT
将保存到 fileoutput.log
并将STDERR
保存到 file error.log
。两个文件都保存在同一目录中script.sh
。
如果你想让你的程序“知道这些文件的路径”,你必须使用绝对路径:
./script.sh > /path/to/output.log 2> /path/to/error.log
答案4
output.log
和文件error.log
在当前目录(即变量值)中创建$PWD
。如果您希望程序稍后使用这些文件,只需在运行脚本之前将其目录保存在变量中即可。这是一个例子:
OUTDIR=$PWD
./script.sh 1> output.log 2> error.log
# Whatever you want to do else ...
echo The output file : =======
cat $OUTDIR/output.log
echo =========================
echo
echo The errors file : =======
cat $OUTDIR/error.log
echo =========================