仓库源文站点原文

终端,控制台和外壳

一些概念

treminal , tty , console 是一开始都是硬件的概念。

一台电脑只有一个 console ,一般有电源开关等硬件操作的, 一台电脑可以有很多个 terminal 。 terminal 是负责 shell 的输入和输出。 console 是一个特殊的 terminal ,就是一个多了电源开关等硬件操作的 terminal 。 tty 是电传打印机。电传打印机是一种把键盘作为输入,纸带作为输出的硬件,是一种 terminal 。 一开始 terminal 就是指 tty 。 后来出现了使用显示器输出的 terminal 。使用显示器输出的 terminal 被称为 video terminal 简称 vt 。

旧时代的大型电脑为了能让多个用户可以同时使用,会提供多个物理终端。

软件意义上的终端出现,是为了让个人电脑的用户可以直接使用他的个人电脑来与大型计算机联系,而不必使用专门的物理终端。 现在的终端会被称为 emulator treminal 或 virtual terminal 。 因为现在已经没有物理意义上的终端了,都是由软件实现。 现在的 treminal , tty , console 都是指一种可以用来显示 shell 的软件, shell 可以是本地的也可以是远程的。

shell 是软件的概念。 shell 负责接收外部输入,调用各种程序或系统命令,然后输出结果。简单但不严谨的解释,负责人机交互的可以称为 shell ,负责显示 shell 的可以称为 terminal 。 shell 通常是指命令行解释器,但图形界面一样可以有 GUI shll ,例如 Windows 的 explorer.exe 。 shell 通常会被翻译成 外壳 或 壳层。 shell 的概念其实是相对于操作系统内核 (kernel) 而言的。

shell 还可以分为 interactive 和 non-interactive 直接输入的命令运行在 interactive shell 上, shell 脚本代码就运行在 non-interactive shell 中。

词汇表

linux 的 tty 子系统

在 linux 或其它 unix like 系统中, tty 就是指终端,不论是软件意义上的或硬件意义上的。

传说,第一个 Unix 终端是一个名字为 ASR33 的电传打字机,而电传打字机的英文单词为 Teletype 或Teletypewritter ,缩写为 tty 。之后终端设备都被称为 tty 设备。 又因为早期的 tty 都是通过串口和主机连接,所以串口设备也是用 tty 来表示的。

在不看源码的前提下,其实很难彻底理解 tty 子系统

物理终端

物理终端 <---> UART 驱动 <---> LDISC <---> tty 驱动 <---> shell

tty 子系统其实就是指内核中的 LDISC 和 tty 驱动。 这两部分在后续的 虚拟终端 和 伪终端 中始终并没有改动。

UART 是一个串口的通讯协议。 UART 是一个 串行 全双工 异步 的通讯协议。 UART 驱动的主要作用就是把字符串转换为 UART 数据包或把 UART 数据包转换成字符串。

LDISC 是 Line discipline 线路规程;行控制;行规程;线路规则;行规则。这个词汇似乎没有统一的中文翻译,所以还是用英文缩写 LDISC 来表示。 LDISC 的主要作用是解释终端中的各种控制字符或特殊字符, 例如 backspace , ctrl+c 这些。 LDISC 也可以不解释直接传输 raw 字符串。 可以在命令行里用 stty -a 查看 tty 的设置,会输出需要转义的字符。

事实上,只要驱动能正确地和 LDISC 通讯就可以了,只是早期的 物理终端 都使用 UART ,后期有物理终端使用其它的接口,例如 ttyUSB , ttySAC 这样的。 后续出现的 虚拟终端 和 伪终端 都是替换了 UART 驱动 , LDISC 和 tty 驱动 并没有大的改动。

虚拟终端

显示器 和 键盘 <---> 显示器驱动 和 键盘驱动  <---> emulator treminal <---> LDISC <---> tty 驱动 <---> shell

虚拟终端 和 物理终端 没有太多的区别,主要是 UART 驱动 被替换成 emulator treminal 。 这里的 emulator treminal 是由内核实现的。

这种由内核实现的终端模拟器并不灵活。 因为,既然是终端模拟器,模拟的就是一个硬件上的终端。 在内核态的终端模拟器如果要添加对新出现的终端的支持,就只能修改内核源码或添加额外的内核模块。 (二十世纪八九十年代可没有 eBPF )

新的终端类型还在出现,只是现在的新终端更多是指新的终端显示规范(例如支持 utf-8 字符集,支持更大的色域这类),而不是指物理意义上的新终端设备。

可以使用 toe -a 来查看支持的终端类型。 可以使用 infocmp 比较两种终端类型的差异,例如这样 infocmp xterm xterm-vt52

虚拟终端 一定是运行在本地的。

虚拟终端 有时又会被成为虚拟控制台(virtual console), 又或者被称为控制台(console), 又或者被称为系统控制台(system console)。

在虚拟机或物理机启动的 linux 系统,系统启动完后,出现的就是 虚拟终端 的界面。 对于绝大多数 linux 发行版而言,系统启动后会默认启动 6 个虚拟终端。 可以使用 Ctrl+Alt+Fx 来切换虚拟终端。 例如 Ctrl+Alt+F2 能切换到 tty2 , Ctrl+Alt+F2 能切换到 tty3 。 通常 tty7 是图形桌面环境,同样可以使用 Ctrl+Alt+Fx 来切换 (前提是系统有正确地安装图形桌面环境)

打开这个文件 /etc/systemd/logind.conf , 修改这个值 NAutoVTs , 就能修改系统启动时的虚拟控制台数量。

现在的控制台其实就是指一个拥有更多权限的终端。 只有物理终端或虚拟终端才有可能成为控制台。 控制台其实是指当前活跃的虚拟终端。 控制台能接收来自内核的日志信息和告警信息。

伪终端

emulator treminal <---> pty master <---> LDISC <---> pty slave <---> shell

这里的 pty master 和 pty slave 是由 pty 驱动实现的。 pty master 和 pty slave 有时也被称为 pty pair。 但这里的 emulator treminal 是运行在用户态的。

伪终端大概有三类使用场景

  1. 图形界面里的终端模拟软件,例如 xterm 和 gnome-terminal
  2. 远程 shell ,例如 通过 telnet 或 ssh 登录服务器
  3. 终端复用软件,例如 screen 和 tmux

伪终端大概又分成两种

在伪终端中的 pty slave 就是一个普通的 tty 驱动,只是换了一个名称而已。

图形界面 和 tty 子系统有密切的联系。 进程管理 和 tty 子系统有密切的关系。 这两个是比较大的问题,基本可以再写一篇文章来描述。

使用 who 命令找到当前系统登录的用户以及其所在的终端

/dev 目录下的 tty 文件

tty 驱动

pty master

特殊的

在驱动程序中,可以通过 register_console() 函数注册将自身注册成为console 。 如果有多个设备都将自己注册为 console 的时候, 那么默认的 console 就是最后一个注册的设备。 但如果启动参数中指定了【 console=设备名 】的话, 那这个参数中的设备才是默认的 console 。 不同的发行版修改启动参数的方式似乎是不一样的。

查看 tty0 是指向哪个 tty 的

cat /sys/devices/virtual/tty/tty0/active

查看活跃的 console

cat /sys/devices/virtual/tty/console/active

查看内核参数

sysctl -a
<!-- ioctl 似乎是一个很重要的 系统调用 在不少的文章里反复出现 似乎还需要补上各种终端运行时的流程图 -->

telnet 和 ssh 远程连接的大致过程

参考

各种 unix like 的外壳

全称 简称 备注
thompson shell sh 第一个 unix like shell 。1971年至1975年随 Unix 第一版至第六版发布
borune shell sh 1978年随Version 7 Unix首次发布
borune again shell bash 在1987年由布莱恩·福克斯(Brian Fox)为了GNU计划而编写,是当前最常用的 shell
almquist shell ash 派生于 borune shell ,最初作为 bsd 的 shell ,目前已不再被广泛使用
debian almquist shell dash 派生于 almquist shell ,是 debian 的 shell
c shell csh 语法类似于C语言,c shell 是第一个实现了 job controller 的 shell , c shell 目前已不再被广泛使用
tenex c shell tcsh csh 的增强版, FreeBSD 中的默认 shell
korn shell ksh AIX 中的默认 shell ,兼容 borune shell ,同时加入了一些 c shell 的特性
zsh zsh zsh 对 borune shell 做出了大量改进,同时加入了 bash , ksh 及 tcsh 的某些功能。 zsh 现在是 mac 的默认 shell
friendly interactive shell fish fish 的语法既不派生于 borune shell 也不派生于 c shell ,故被分类为一种“外来” shell 。

各种 shell 的发展脉络

+------------------------------------------------------------------------------------------------------------------+
|                                                                                                                  |
|                                                                                                                  |
|                                                                                                                  |
|                          Thompson shell                                                                          |
|                 +----------+     +--------------------------+                                                    |
|                 |                                           |                                                    |
|                 |                                           |                                                    |
|                 v                                           v                                                    |
|                c shell                                  Bourne shell                                             |
|                  +                                           +                                                   |
|                  |                                           |                                                   |
|            +-----+-----------------+                         | +------------------>                              |
|            |                       |                         |                    |                              |
|            v                       v                         v                    v                              |
|     tenex c shell             korn shell           Bourne-Again shell       almquist shell                       |
|         +                          +                        +                     +                              |
|         |                          |                        |                     |                              |
|         +--------------------------v--------+---------------+                     |                              |
|                                             |                                     v                              |
|                                             |                              debian almquist shell                 |
|                                             v                                                                    |
|                                            zsh                                                       fish        |
|                                                                                                                  |
+------------------------------------------------------------------------------------------------------------------+

sh 通常是指遵循 POSIX 标准的 shell 。 bash 有 3 种方式使其遵循 POSIX 标准 https://www.gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html

通常情况下 shell 脚本以这句开头 #!/bin/sh ,就是表示这份脚本遵循 POSIX 标准。 如果想脚本足够的通用,最好不要用 bash 的语法。

除了 POSIX 还, shell 还有两份标准, IEEE 1003.1 和 ISO/IEC 9945 。

现在绝大部分 unix like 系统中, /bin/sh 和 /usr/bin/sh 一般都是链接文件,指向真正的默认 shell 。 现在绝大部分 shell 都兼容 POSIX 标准,但同时又会有一些自己的拓展。

新版本的 powershell 也能运行在 linux 上。

查看系统可用的 Shell

cat /etc/shells
<!-- 可以用在 mac 和 ubuntu -->

查看当前 shell

echo $SHELL
<!-- Shell 与 REPL ? -->

bash

在简体中文互联网中,关于 bash 的启动过程有非常多的文章。 笔者看得有一点混乱,于是还是看了 bash 的文档再结合自己的实践,重新总结一次。

bash 的文档

bash 的运行模式

从文档中看 bash 有四种运行模式(忽略 sh 的兼容) |||| |-|-|-| ||interactive|non-interactive| |login|login interactive|login non-interactive| |no-login|no-login interactive|no-login non-interactive|

直接启动的 bash 会以 no-login interactive shell 运行。

bash
# 这时会进入一个新的 shell
# 这个旧的 shell 就是新的 shell 的父进程

加上 --login 或 -l 参数就能以 login interactive shell 运行。

bash --login
bash -l

如果加上了脚本路径或命令,那么默认会以 no-login non-interactive shell 运行。

bash -c "echo hello"
bash ./test.sh

如果 shell 脚本里没有声明 #! 或声明时没有带参数,那么也是以 no-login non-interactive 运行的。 但如果声明相应的参数,那么也可以以对应的模式运行。

#!/bin/bash # 没有声明或不带参数 以 no-login non-interactive 运行
#!/bin/bash -i # 以 no-login interactive 运行
#!/bin/bash -l # 以 login non-interactive 运行
#!/bin/bash -li # 以 login interactive 运行

login 或 no-login 似乎都没有什么权限的限制。 在 no-login shell 中可以启动 login shell , 在 login shell 中也可以启动 no-login shell 。

可以用这样的命令来判断 shell 是不是 login shell

shopt login_shell
# login shell 会输出 login_shell     on
# no-login shell 会输出 login_shell     off

可以用这样的命令来判断 shell 是不是 interactive shell

echo $-
echo $PS1
# echo $- 如果输出的结果中包含 i ,则是 interactive shell ,否则就是 non-interactive shell
# echo $PS1 如果输出的结果不为空,则是 interactive shell ,否则就是 non-interactive shell

可以用这样的命令来观察 bash 的运行模式和各个运行模式下加载的文件,注意修改 --login 和 -i 参数

strace -f -e open,execve bash --login -i -c "shopt login_shell;echo \$-" 2>&1 | grep -E "(profile|bashrc|login_shell|^h)"
<!-- bash -c "echo \$-;echo \$PS1;shopt login_shell" no-login non-interactive shell bash --login -c "echo \$-;echo \$PS1;shopt login_shell" login non-interactive shell bash -i -c "echo \$-;echo \$PS1;shopt login_shell" no-login interactive shell bash --login -i -c "echo \$-;echo \$PS1;shopt login_shell" login interactive shell 直接运行 bash 会进入 no-login interactive shell 宿主 bash 的设置 四种 脚本中的设置 也是四种 这样就有 16 种情况了。。。 echo $- echo $PS1 shopt login_shell echo "no" #!/bin/bash echo $- echo $PS1 shopt login_shell echo "/bin/bash" #!/bin/bash -i echo $- echo $PS1 shopt login_shell echo "/bin/bash -i" #!/bin/bash --login echo $- echo $PS1 shopt login_shell echo "/bin/bash --login" #!/bin/bash -li echo $- echo $PS1 shopt login_shell echo "/bin/bash -li" ./0.sh;./1.sh;./2.sh;./3.sh;./4.sh -->

bash 的启动脚本

从文档中看 bash 在启动时会执行两种脚本

除了 startup file 和 initialization file 之外,还有一个 ~/.bash_logout 脚本,用于 login shell 退出时执行的。

startup file 和 initialization file 通常会用于设置一些环境变量或命令的别名或一些初始化用的脚本。 命令别名的例子 alias rm='rm -i' 。

在文档中还提及到了 ~/.bash_history 和 ~/.inputrc 。 一个是记录命令行的历史记录, 一个是用于 readline 的初始化。 如果 ~/.inputrc 不存在,则会尝试读取 /etc/.inputrc 。

文档里提及到的文件基本就这几个了

这几个文件在文档里没有提及,但却经常出现在各类文章中

这几个文件本质上都是通过 startup file 或 initialization file 执行的。 在约定俗成的规则中, /etc/.bashrc 由 ~/.bashrc 调用, /etc/profile.d/*.sh 和 /etc/profile.d/sh.local 由 /etc/profile 调用。 具体的调用过程看一下 startup file 和 initialization file 就知道了。 不同的发行版执行的过程可能会不一样,所以最保险的方式还是自己看一下 startup file 和 initialization file 。 在互联网的一些文章里,会把 /etc/profile.d/*.sh 的脚本描述成开机启动的脚本或终端启动时的脚本或用户登录时的脚本, 从效果上看似乎也是正确的。 还有一个需要注意的地方是, startup file 和 initialization file 其实是可以相互调用的, 例如 在 ~/.bash_profile 里调用 ~/.bashrc , 在 /etc/bashrc 里调用 /etc/profile.d/*.sh 。

PS . 其它 shell 的 initialization file 也是以 rc 结尾的

<!-- 通过文件锁,确定脚本只执行一次? -->

需要特别注意的是 sh 的启动过程和 bash 是不一样的。笔者平时工作时用不到 sh ,就不探究这个了。

<!-- 内置变量 $0, $#, $*, $@, $?, $$, $_, $-, $PS1 $0:表示当前脚本的文件名。 $n:表示传递给脚本或函数的第 n 个参数,n 是一个数字,从 1 开始。例如,第一个参数是$1,第二个参数是$2。 $#:表示传递给脚本或函数的参数个数。 $*:传递给脚本或函数的所有参数。 $@:传递给脚本或函数的所有参数。 $?:上个命令的退出状态,或函数的返回值。 $$:当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 $* 和 $@ 的区别$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。 但是当它们被双引号(" ")包含时, "$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数; "$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。 加上双引号后, “$*” 表示的是用内部分割符 IFS (Internal Field Separator) 连接起来的一个完整的统一字符串 $@ 加不加双引号,都一样的 多数情况下 IFS 都是空格 但也可以单独地设置 IFS export IFS=% $- 这个变量表示当前 Shell 的选项,也就是使用 set 命令设置的标志 $_ (下划线) 表示的是打印上一个输入参数行, 当这个命令在开头时, 打印输出文档的绝对路径名. PS1 是Prompt String 1的缩写, PS1是交互式的shell用于控制用户输入提示信息的环境变量, 类似的环境变量还有PS2、PS3和PS4。 通常会显示 用户名 主机名 当前目录 是否是root用户,如果不是root用户会显示#,否则显示$ \u@\h:\w\$ 还可以显示更多信息,例如 日期 时间 这类 还可以加上颜色,这和普通的输出带颜色的字符是一样的,同样需要终端支持 环境变量 用途 PS1 交互式脚本等待用户输入时的提示信息 PS2 一条命令没有结束的时候的连接性的提示(比如使用\将一行复杂的命令使用多行连接起来的场合) 默认值 > PS3 和select命令结合使用 默认值是 #? PS4 调试时的行前显示内容 默认值是 + echo $PS1; echo $PS2; echo $PS3; echo $PS4; $PROMPT_COMMAND; $PROMPT_COMMAND $PROMPT_COMMAND 是 Bash shell 中的一个环境变量。当设置了这个变量时,Bash 会在每次主提示符 $PS1 出现之前执行其值指定的命令。这为用户提供了一个强大的机制,用于增强和定制他们的命令行体验。 例如,可以这样设置 PROMPT_COMMAND: export PROMPT_COMMAND='echo -n $(date +%T)' 这样,每次得到一个新的命令提示符之前,都会先显示当前的时间。 $PROMPT_COMMAND 可以包含一系列命令,这些命令会被依次执行。它也可以用来调用外部脚本或函数,从而在提示符中显示更复杂的信息。 提示符 prompt 和 PS系列的变量相关 `PS3` 是 Bash shell 中的一个环境变量,它用于定义在使用 `select` 命令构建菜单选择时显示的提示符。`select` 命令允许用户从一组选项中选择一个选项,并执行相应的操作。 当运行包含 `select` 命令的脚本时,`PS3` 提示符会显示,并等待用户输入。默认情况下,`PS3` 的值是 `#? `,但您可以根据需要自定义这个提示符。 以下是一个使用 `PS3` 的 Bash 脚本示例: ```bash #!/bin/bash echo "请选择一个选项:" PS3="请输入您的选择: " select option in "选项1" "选项2" "选项3" "退出" do case $option in "选项1") echo "您选择了选项1" ;; "选项2") echo "您选择了选项2" ;; "选项3") echo "您选择了选项3" ;; "退出") break ;; *) echo "无效的选择" ;; esac done ``` 这段脚本可以直接在命令行里运行,但要复制后一次性输入 bash 提示符的文档 https://www.gnu.org/software/bash/manual/html_node/Controlling-the-Prompt.html 命令行提示符 PS 是 Prompt String 的缩写 PS1 主提示符 用户名 主机名 路径 PS2 二级提示符 PS3 PS4 内置命令 程序 脚本 别名 $! $PPID $SHELL $BASH $BASH_VERSION $EUID 与 $UID $GROUPS $BASHPID $HOME $HOMENAME $PWD $OLDPWD $IFS 内部域分隔符。这个变量用来决定 Bash 在解释字符串时如何识别域,或者单词边界。 $IFS默认为空白(空格, 制表符,和换行符) 内置变量 和 环境变量 有什么区别? 在 Bash 中,内置变量和环境变量的主要区别在于它们的作用域和持久性。 内置变量(Shell Variables): 仅在当前 shell 中有效。 不会被 shell 的子进程继承。 通常用于临时存储数据,如当前工作目录。 例如,定义一个内置变量:SOME_VAR='value'。 环境变量(Environment Variables): 对当前 shell 及其所有子进程都有效。 用于在进程间传递信息。 通过使用 export 命令,可以将内置变量提升为环境变量,使其对子进程可见。 例如,将内置变量转换为环境变量:export SOME_VAR。 简而言之,环境变量可以在多个程序之间共享,而内置变量则限于当前 shell。 https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Variables.html https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html 在 Shell 中,可以通过几种方法来判断终端是否支持显示颜色: 使用 tput 命令:tput colors 会返回终端支持的颜色数量。如果返回值大于8,则通常表示支持颜色显示。 检查 $TERM 环境变量:某些 $TERM 值(如 xterm-256color)表明终端支持颜色。可以通过 echo $TERM 来查看当前终端类型。 运行颜色测试脚本:有些脚本可以生成颜色图案,通过观察输出是否包含连续的色彩,可以判断终端是否支持真彩色(24位颜色)。 终端文档和支持信息:查阅终端模拟器的文档或支持信息,了解其颜色支持情况。 查看所有别名 alias 判断一个命令是不是别名 type 命令 新建一个别名 alias 别名="实际的命令,可以加上参数" 删除一个别名 unalias 别名 -->

Windows 的外壳和脚本

Windows 的外壳和终端

一开始的 dos 和 9x 的系统里, COMMAND.COM 既是也是 shell 和 终端。 这是一个单一的程序。 shell 和 终端 混合在一起实现的。 并没有象 unix like 的系统里分开实现。

和 COMMAND.COM 一样 , cmd.exe 和 早期的 PowerShell 既是也是 shell 和 终端。 笔者留意到,在中文互联网里,关于 cmd 到底是 shell 还是终端,有非常多的讨论。

cmd.exe 一直没更新。 PowerShell 从 PowerShell core 开始,就是一个纯粹的 shell ,里面没有包含终端的实现代码。 旧版的 PowerShell 一直存在并没有被替换掉。 所以,现在的 windows 里其实是有三种 shell 的, cmd.exe , powershell.exe , pwsh.exe 。

windows terminal 是微软在 windows10 之后推出的终端, 是一个纯粹的终端,没有包含 shell ,可以用来显示各种 shell 。 笔者认为现代化的 Windows 开发应该使用 windows terminal 和 PowerShell (7.0及之后的版本)。 bat , JScript , VBScript , Windows PowerShell 这些都是过时的技术了。

这一些系列的文章详细地描述了 Windows 外壳和终端的发展过程

脚本语言

当前的 windows 中一共有四种预装的脚本语言

bat

bat 在 dos 时代就已经存在,是最古老的脚本语言。

在 dos 或 9x 的系统中, bat 的执行主体是 COMMAND.COM 。

在 nt 系统中, bat 的执行主体是 cmd.exe 。

bat 的脚本文件有两种后缀名 bat 和 cmd 。 在 nt 系统中无论哪种后缀名都是用 cmd.exe 执行的。 在 dos 或 9x 的系统中, bat 的后缀会通过 COMMAND.COM 执行,但 cmd 的后缀会无法执行。

在当前的 windows 系统中,两种后缀名是完全没有区别的。

自从 PowerShell 出现后,微软就不更新 bat 了,一直鼓励用户改用 PowerShell 。

JScript 和 VBScript

JScript 和 VBScript 是在 1996 年的 Professional Developers Conference (PDC) 发布。 一开始是应用在 IE3 上的脚本语言,对标的是网景的 JavaScript 。 JScript 能兼容当时的 JavaScript , Jscript 出现的初衷是为了兼容已经存在的使用 JavaScript 的网站。

JScript 和 VBScript 需要运行在宿主内, Windows 提供了几种宿主环境

自从 PowerShell 出现后,微软就不建议用户使用 JScript 和 VBScript 作为系统脚本, 一直鼓励用户改用 PowerShell 。

PowerShell

PowerShell 在 2006 年首次出现在 vista 中,也可以安装在 xp sp2 和 xp sp3 中。 PowerShell 是微软为了替换 bat 和 JScript 和 VBScript 而推出的。

PowerShell 1.0 - 5.1 是基于 .NET Framework 的,所以只能运行在 windows 上。

PowerShell 6.0 - 6.1 是基于 .NET Core 的,所以可以跨平台。这时的 PowerShell 还改名成 PowerShell Core ,能和 PowerShell 同时安装在一个系统里。

PowerShell 7.0 之后, PowerShell 再一次改名,基于 .NET Core 3.1 (后来 .NET Core 直接改名成 .NET) 的 PowerShell 就叫做 PowerShell , PowerShell 5.1 及之前的版本叫做 Windows PowerShell 。

真不愧是微软改名部

PowerShell 7.0 之后,除了改名, 还有把可执行文件的文件名重命名为 pwsh 。 第一个默认参数修改为 -File ,可以象这样执行脚本 pwsh script.ps1 。 可以加上 -i 进入交互模式。 这几项的修改,使得 PowerShell 7.0 的行为上更接近 unix like 里的 shell 。 从 PowerShell 7.0 开始, PowerShell 就可以作为 unix like 系统里的默认 shell 。

从微软的文档来看, PowerShell 会按照微软的软件生命周期策略更新下去。 而 Windows PowerShell 则会象 cmd.exe 一样,会保留在系统里但不会有更新。

git for windows 中 的 bash

git for windows 中 的 bash 主要分成三部分

多数情况下

git for windows 安装完成后,加入到环境变量 path 的是这个目录

git for windows 的安装根目录/cmd

在命令行里直接运行 bash 其实是运行 wsl2 里的 bash

为了能直接在命令行里使用 git for windows 中 的 bash ,可以尝试这样做, 在这个目录 git for windows 的安装根目录/cmd 下新建一个名为 gitbash.bat 的文件,并写入以下内容, 最后就可以在命令行里用 gitbash 命令来运行 git for windows 中 的 bash

@echo off
%~dp0..\bin\bash.exe -l %*

可以用这样的命令来查看帮助 git help git-bash 会在默认浏览器打开一个帮助页面

git-bash.exe 也是只运行 mintty.exe 。 mintty.exe 可以手工加上命令行参数,达到和 git-bash.exe 一样的效果。 git-bash 的参数其实可以直接用在 mintty.exe 里

参考

wsl 中的 bash

各种文件后缀

后缀 备注
.vbs VBScript
.js JScript
.vbe VBScript Encoded , 已编码的 VBScript 脚本文件
.jse JScript Encoded , 已编码的 JScript 脚本文件
.ws Windows Scripting
.wsc Windows Scripting Component
.sct Windows Scripting Component
.wsf Windows Scripting File
.wsh Windows Scripting Host
.hta HTML Application
.cmd Command Prompt 命令提示符
.bat batch 批处理文件
.ps1 Windows PowerShell 脚本
.pac Proxy Auto-Configuration
.asp Active Server Page
.aspx Active Server Page Extended
.srf server response file
.htm html
.xht xhtml
.shtm 包含服务器端指令的 HTML 文件
.shtml 包含服务器端指令的 HTML 文件
.stm 包含服务器端指令的 HTML 文件
.chm Compiled HTML Help 编译的HTML帮助文件
.cpl Control PaneL extension 控制面板扩展程序
.msc MicoSoft management Console 微软管理控制台
.exe executable 可执行程序

在 dos 里,文件的后缀名最多是 3 个字母, 在 9x 和 nt 的系统里延续了这个设定(其实早就没有长度限制了) 。 所以会出现一些和 unix like 不一样的缩写,例如

windows 中的 telnet

参考

windows console 的文档 https://docs.microsoft.com/zh-cn/windows/console/

描述 windows 控制台 的发展历程 https://docs.microsoft.com/zh-cn/windows/console/ecosystem-roadmap#major-historical-milestones

windows terminal 的文档 https://docs.microsoft.com/zh-CN/windows/terminal/

Microsoft Windows 脚本技术 https://www.jb51.net/shouce/script56/

JScript 和 VBScript 的文档 https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/

JScript 和 VBScript 的起源 https://webdevelopmenthistory.com/1996-microsoft-activates-the-internet-with-activex-jscript/

微软的文章 这里描述了 cmd 和 bat 的区别 https://learn.microsoft.com/en-us/previous-versions//cc723564(v=technet.10)?redirectedfrom=MSDN

<!-- VTNT 应该是 微软 自己的 vt 规范 https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-tvtt/e18d3a32-3eb0-4788-8ba3-9043bc6d9708 vtnt 中的 nt 是什么的缩写 猜测是来自 windows nt 的 也就是 New Technology 的意思 windows 上的 telnet 支持这 4 种终端 ansi | vt100 | vt52 | vtnt https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/telnet https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/telnet-set#parameters 查看 powershell 版本 $PSVersionTable 查看 bash 版本 bash -version 查看 cmd 版本 ver 判断当前 shell 的类型 echo $PSVersionTable echo $SHELL echo %cd% $PSVersionTable sh --version bash --version sh -c "$SHELL --version" ver 程序名 程序所在目录 程序运行目录 当前工作目录 进程名 完整的命令行 通过环境变量获取 通过系统api获取 脚本文件名 echo %0 Write-Host $MyInvocation.MyCommand.Name echo $(basename $0) 脚本所在目录 echo %~dp0 Write-Host $PSScriptRoot Write-Host $MyInvocation.MyCommand.Source 这一句兼容性好一点 echo $(dirname $(readlink -f $0)) 脚本运行目录 脚本运行时不要切换工作目录,通过 %CD% 获取 Write-Host $PWD echo $PWD 当前工作目录 echo %CD% Write-Host $(Get-Location).Path echo $(pwd) powershell 切换到脚本所在目录 Set-Location $PSScriptRoot 在 cmd 中可以像这样获取完整的命令行 echo %CMDCMDLINE% basename 获取路径中的文件名 dirname 获取路径中的目录名 这四种脚本运行方式的差异 ./test.sh source ./test.sh bash ./test.sh /usr/bin/bash ./test.sh 这种问题出现的原因是什么? stdin: is not a tty 为什么再输入一次 bash 就没问题了? 语法关系图中的符号? -->