shell 脚本如何知道它是在 shell 的 CLI 上调用的?

kjo*_*kjo 4 bash zsh

我想实现一个zsh脚本,其行为取决于是否直接在 shell 的 CLI 上调用。

我一开始以为脚本可以通过查找 的i值来做到这一点$-,但我错了。

事实上,当我从命令行运行下面的脚本时

#!/bin/zsh
printf -- '%s\n' "$-"
Run Code Online (Sandbox Code Playgroud)

...我得到的输出不包括i1。

我的 shell 脚本还有其他方法可以找出这些信息吗?

注意:zsh虽然我现在正在写剧本,但我也想知道bash剧本的答案是什么。如果答案取决于OS,我主要对 Linux 和 Darwin 感兴趣。


1569X如果有人想知道的话,脚本的输出是。

Sté*_*las 6

$-包含i何时 shell 本身是交互式 shell、何时发出提示、允许您在终端上输入和编辑命令行、执行作业控制等。

\n

脚本通常由非交互式 shell 解释。如果您将脚本调用为./the-script或zsh ./the-script,则该zsh实例不是交互式的。交互式 shell 解释脚本的唯一时间是当您告诉交互式 shell 使用source ./the-script或 之类的内容解释其中的代码时eval "$(<the-script)"。

\n

在这里,在我看来,您似乎更想知道脚本是否是从终端内调用的,就像您想知道用户是否可以通过终端与其进行交互一样。

\n

[/命令test(内置于 zsh 和 bash 以及大多数其他类似 Bourne 的 shell 中,但也应该作为 POSIX 系统上的独立命令存在)可以通过其-t运算符告诉您。该-t运算符采用 one\xc2\xb9 数字参数,它是一个文件描述符。

\n

[ -t 0 ]或者test -t 0如果文件描述符 0 在终端设备上打开,则返回 true。文件描述符0是标准输入,1是标准输出,2是标准错误,所有其他fd没有任何保留的特殊含义。

\n

所以在你的脚本中,你可以这样做:

\n
if [ -t 0 ]; then\n  echo "I (this script) am taking input from a terminal, so likely from a user"\nfi\n\nif [ -t 1 ]; then\n  echo "My output goes to a terminal device, so will likely be seen by a user"\n  [ -t 2 ] && echo errors also go there.\nfi\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想检查脚本是否从终端会话中运行,即使其输入和/或输出可能被重定向到终端设备之外的其他文件,您可以检查是否有“终端附加到脚本” s 会话类似于:

\n
if { true <> /dev/tty; } 2> /dev/null; then\n  echo "there\'s a terminal device attached to my session"\nfi\n
Run Code Online (Sandbox Code Playgroud)\n

如果你想知道它是什么终端设备:

\n
tty_on_stdin=$(tty)\n
Run Code Online (Sandbox Code Playgroud)\n
tty_on_stderr=$(tty <&2)\n
Run Code Online (Sandbox Code Playgroud)\n
{ tty_on_stdout=$(tty <&3); } 3>&1\n
Run Code Online (Sandbox Code Playgroud)\n

(请参阅此后续问答,了解与 fd 3 的小舞蹈背后的原因)。

\n
controlling_tty=$(LC_ALL=C ps -o tty= -p "$$")\n[ "$controlling_tty" = \'?\' ] || controlling_tty=/dev/$controlling_tty\n
Run Code Online (Sandbox Code Playgroud)\n
\n

\xc2\xb9 在早期的test/[实现中,该参数是可选的并且[ -t ]是[ -t 1 ]. POSIX 不再允许这样做,因为[ "$var" ]这与[ -n "$var" ]. 尽管如此,你会发现ksh93仍然检查 stdout 是否转到 tty,[ -t ]尽管只有当-t是字面量时;var=-t; [ "$var" ]即使 stdout 不在终端上也返回 true。

\n