确定Shell输入来自终端还是管道

作者:Mitch Frazier

前几天在编写一个小脚本时,需要确定脚本的输入来自管道还是终端。 看起来这件事情应该很简单,但是没有任何东西能立即想到,快速的互联网搜索也没有提供太大的帮助。 经过一番思考,我想出了两种解决方案:使用stat命令和使用来自 proc 文件系统的信息。

第一个解决方案使用stat命令来确定连接到标准输入的文件类型。 首先,我们找出什么连接到标准输入

stdin="$(ls -l /dev/fd/0)"
stdin="${stdin/*-> /}"

文件/dev/fd/0是标准输入,它是一个符号链接。 所以我们使用ls来获取它链接到的文件。 然后我们删除所有匹配*->从值的前面开始,剩下的就是链接到的文件。

现在我们使用stat来获取文件类型

ftype="$(stat --printf=%F $stdin)"

然后我们只需测试文件类型

if   [[ "$ftype" == 'character special file' ]]; then
    echo Terminal
elif [[ "$ftype" == 'regular file' ]]; then
    echo Pipe: $stdin
else
    echo Unknown: $stdin
fi

我们可以通过以下方式进行测试:

$ sh ckpipe.sh
Terminal
$ sh ckpipe.sh <ckpipe.sh
Pipe: .../ckpipe/ckpipe.sh

我想出的下一个解决方案涉及使用来自 proc 文件系统的信息。 在 proc 文件系统中,每个进程的文件都出现在目录/proc/PROCESS_ID/fd中(对于当前进程,可以使用特殊目录/proc/self/fd

$ ls -la /proc/self/fd
total 0
dr-x------ 2 mitch users  0 2010-02-10 11:04 .
dr-xr-xr-x 7 mitch users  0 2010-02-10 11:04 ..
lrwx------ 1 mitch users 64 2010-02-10 11:04 0 -> /dev/pts/2
lrwx------ 1 mitch users 64 2010-02-10 11:04 1 -> /dev/pts/2
lrwx------ 1 mitch users 64 2010-02-10 11:04 2 -> /dev/pts/2
lr-x------ 1 mitch users 64 2010-02-10 11:04 3 -> /proc/29328/fd

和以前一样,我们需要链接到的文件的名称,所以我们用以下方式获取它:

stdin="$(ls -l /proc/self/fd/0)"
stdin="${stdin/*-> /}"

从那里,我们可以测试它是否链接到/dev/pts文件

if [[ "$stdin" =~ ^/dev/pts/[0-9] ]]; then
    echo Terminal
else
    echo Pipe: $stdin
fi

我们用同样的方式测试它

$ sh ckpipe2.sh
Terminal
$ sh ckpipe2.sh <ckpipe2.sh
Pipe: .../ckpipe/ckpipe2.sh

Mitch Frazier 是艾默生电气公司的嵌入式系统程序员。 自 2000 年代初以来,Mitch 一直是Linux Journal的贡献者和朋友。

加载 Disqus 评论