[更新]感谢大家迄今为止的评论和想法。基于这些,我更新并扩展了原始问题,添加了更多信息和示例。
我们假设目前没有实际用例,唯一的目标是了解这是否可能,如果可能,如何实现。
我想运行一个脚本并确定它是直接由用户运行还是在另一个脚本中调用。我还想检查它是否有来源。
这意味着有 4 个主要测试用例:
- 在终端运行,无需来源
- 在终端运行,来源
- 在脚本中运行(不在终端中),没有来源
- 在脚本中运行(不在终端中),来源
我的大脑已经非常生疏,谷歌和 ChatGPT 为我提供了一系列检查,以测试它们是否在终端中运行,但没有一个能够在所有 4 个调用中给出正确的结果。
测试已完成check-term
并且有一个调用脚本check-term-driver
,均在下面。
如果我按照上面给出的顺序运行 4 个测试用例,对于给定的正确测试号(假设测试号为 n),我希望输出为:
n: in terminal: not sourced: ...
n: in terminal: sourced: ...
n: not in terminal: not sourced: ...
n: not in terminal: sourced: ...
在所有 4 个调用中,对是否存在来源的测试都是正确的。
检查项脚本:
#!/usr/bin/env bash
IS_TERMINAL=true
NOT_TERMINAL=false
TEST_NUM=0
SOURCED_TEXT="undefined"
print_result() {
TEST_NUM=$((TEST_NUM + 1))
if [ "$1" = "$IS_TERMINAL" ]; then
printf "%2d: %-16s %-12s %s\n" "$TEST_NUM" "in terminal:" "$SOURCED_TEXT:" "$2"
else
printf "%2d: %-16s %-12s %s\n" "$TEST_NUM" "not in terminal:" "$SOURCED_TEXT:" "$2"
fi
}
# First check if script is sourced or not.
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
SOURCED_TEXT="not sourced"
else
SOURCED_TEXT="sourced"
fi
# Tests run individually in expanded if/else for clarity.
# - test condition described by the result text.
if [ -t 0 ]; then
print_result "$IS_TERMINAL" "stdin is a terminal"
else
print_result "$NOT_TERMINAL" "stdin is not a terminal"
fi
if [ -t 1 ]; then
print_result "$IS_TERMINAL" "stdout is a terminal"
else
print_result "$NOT_TERMINAL" "stdout is not a terminal"
fi
if [ -t 2 ]; then
print_result "$IS_TERMINAL" "stderr is a terminal"
else
print_result "$NOT_TERMINAL" "stderr is not a terminal"
fi
if [[ "$-" == *i* ]]; then
print_result "$IS_TERMINAL" "interactive shell"
else
print_result "$NOT_TERMINAL" "non-interactive shell"
fi
if [ -n "$(tty)" ]; then
print_result "$IS_TERMINAL" "has a controlling terminal"
else
print_result "$NOT_TERMINAL" "does not have a controlling terminal"
fi
if [ -p /dev/stdin ]; then
print_result "$NOT_TERMINAL" "running in a pipeline"
else
print_result "$IS_TERMINAL" "not running in a pipeline"
fi
if [ -z "$(jobs -p)" ]; then
print_result "$IS_TERMINAL" "running in foreground"
else
print_result "$NOT_TERMINAL" "running in background"
fi
# Get ID of process that started the script.
PPID_VALUE=$(ps -o ppid= -p $$ | awk 'NR==2 {print $1}')
# If ID can't be found, give it a default value.
[ -z "$PPID_VALUE" ] && PPID_VALUE=1
# Check if attached or not.
if [ "$PPID_VALUE" -ne 1 ]; then
print_result "$IS_TERMINAL" "attached to a parent process"
else
print_result "$NOT_TERMINAL" "detached from a parent process"
fi
# Check if the parent process is a shell
PARENT_CMD=$(ps -o args= -p "$PPID_VALUE" 2>/dev/null)
if echo "$PARENT_CMD" | grep -qE '/(bash|zsh|sh)(\s|$)'; then
print_result "$IS_TERMINAL" "parent process is a shell"
else
print_result "$NOT_TERMINAL" "parent process is not a shell"
fi
驱动脚本:
#!/usr/bin/env bash
./check-term
echo ""
. ./check-term
使用 bash 版本 3.2.57、4.4.18、5.1.16 和 5.2 进行测试:
bash
注 1:如果二进制文件已重命名且其名称不再包含,则此代码将无法正常工作bash
。注 2:如果从函数执行此代码,则该代码不起作用。
实现此目的最可靠的方法(到目前为止)是使用 pai-to 建议的环境变量;这适用于所有场景,包括嵌套函数调用。
Cyrus 建议的解决方案是独立的(没有外部依赖),如果我确信测试不会在函数中使用,我会使用它。
虽然环境变量解决方案有效,但我仍然更喜欢没有外部依赖的解决方案;如果有人有解决方案,请告诉我。
这是一个使用环境变量的工作演示。
检查期限:
司机: