GUN/Bash 系列,看这一篇就足够了

什么是 Shell ?

  • 一个命令行解释器,用于将用户输入的命令转换为系统操作;
  • Shell 既是交互式命令语言(CMD),也是脚本编程语言(Script);
  • Shell 有很多内置在其源代码中的命令。这些命令是内置的,所以 Shell 不必到磁盘上搜索它们,执行速度因此加快。不同的 Shell 内置命令有所不同;

有哪些 Shell ?

GUI (Graphical User Interface)

  • GNOME
  • KDE
  • ……

CLI (Command Line Interface)

CLI 实际上是一个程序,比如 /bin/bash 。它是一个实实在在的程序,它打印提示符(PROMPT),接受用户输入的命令,分析命令序列并执行然后返回结果。

  • Bourne Shell (sh)
    • 即 sh,是影响最广的 shell 。 1977 年由 Stephen Bourne 在贝尔实验室编写。虽无明文规定,但已成为事实上的标准;
    • 引入了 shell 通用的、基础的功能,例如管道、变量、条件判断、循环等;
    • 全部类 Unix 系统,都至少有一个与 sh 兼容的shell;
    • sh 一般位于 /bin/sh。目前大部分系统,/bin/sh 都是一个链接,指向一个兼容 sh 的、功能更丰富的 shell,如 bash;
  • C Shell (csh)
    • 即 csh,1978年由 Bill Joy 在伯克利大学毕业后编写;
    • Bill Joy,Sun 联合创始人,vi 作者,BSD 作者;
    • csh 更加注重交互式使用而非脚本应用,引入了历史功能、别名、目录栈、作业控制等功能;
  • Bourne Again Shell (bash)
    • 即 bash,1989年由 Brian Fox 为 GNU 项目编写。
    • 综合了 sh、csh、ksh 等各种 shell 的特性;
    • 名字有双关含义,既是字面上的意思,用于替换 sh,也隐含为了 GNU 而 born again 的意思;
    • 是 Mac OS X 和大部分 Linux 发行版的默认 shell;
  • Korn Shell (ksh)
  • Z Shell (zsh)
  • TENEX C Shell (tcsh)
  • ……

GUI 和 CLI 如何切换?

切换到 tty1 ~ tty6 终端:Ctrl + Alt + [F1 ~ F6]

切换到 GUI:Ctrl + Alt + [F7]

启动流程

  1. /bin/login 程序首先会检查 /etc/passwd 文件,在这个文件里包含了用户名、密码和该用户的登录 shell,如 /bin/bash 。 /bin/login 在子进程里用 execve 调用了 /bin/bash 。
  2. /bin/bash 读取 启动文件 并启动
  3. /bin/bash 处理用户键入的命令
  • 在执行磁盘上某个程序时,我们通常不会指定这个程序文件的绝对路径,比如要执行 echo 命令时,我们一般不会输入 /bin/echo ,而仅仅是输入 echo 。那为什么这样 bash 也能够找到 /bin/echo 呢?原因是 Linux 操作系统支持这样一种策略:shell 的一个环境变量 PATH 里头存放了程序的一些路径,当 shell 执行程序时会去这些目录下查找。
  • which 作为 shell(这里特指 bash )的一个内置命令,如果用户输入的命令是磁盘上的某个程序,它会返回这个文件的全路径。

启动文件

交互式 shell

Bash 如何执行它的启动文件?交互式 shell(interactive shell)下需要区分两种情况:

login shell

在下列情况下,我们可以获得一个 login shell:

  • 登录系统时获得的顶层 shell,无论是通过本地终端登录,还是通过网络 ssh 登录。这种情况下获得的 login shell 是一个交互式 shell。
  • 在终端下使用 --login 选项调用 bash,可以获得一个交互式 login shell。
  • 在脚本中使用 --login 选项调用 bash(比如在 shell 脚本第一行做如下指定:#!/bin/bash --login),此时得到一个非交互式的 login shell。
  • 使用 su - 切换到指定用户时,获得此用户的 login shell。如果不使用 -,则获得 non-login shell。

login shell 与 non-login shell 的主要区别在于它们启动时会读取不同的配置文件,从而导致环境不一样。login shell 启动时:

  • 首先读取全局配置:/etc/profile
  • 然后依次查找以下三个文件,读取第一个找到且可读的文件:
    • ~/.bash_profile
    • ~/.bash_login
    • ~/.profile
  • 退出时,读取:~/.bash_logout

no login shell

交互式的 non-login shell 启动时,会读取:

  • ~/.bashrc

通常我们要定制一些配置时(例如 alias 别名),会将配置写在 ~/.bashrc 中,然后在 ~/.bash_profile 中读取 ~/.bashrc,这样可以保证 login shell 和 non-login shell 得到相同的配置:

test -f ~/.bashrc && . ~/.bashrc

至于 /etc/profile 就不要轻易去改啦,毕竟会影响系统全局的配置。

非交互式 shell

  • 当 Bash 以非交互的方式(non-interactive shell)启动时,例如在运行一个 shell 脚本时,它会查找环境变量 BASH_ENV,如果存在则将它的值展开,使用展开的值作为一个文件的名称,读取并执行。 Bash 运作的过程就如同执行了下列命令:

    if [ -n "$BASH_ENV" ]; then 
        . "$BASH_ENV"; 
    fi
    
  • 其它情况:

    Aliases are not expanded when the shell is not interactive.

    Functions are executed in the context of the current shell; no new process is created to interpret them (contrast this with the execution of a shell script).

Shell 语法

引用(Quoting)

引用用于:

  • 阻止对特殊字符的处理。
  • 阻止保留字被识别。
  • 阻止参数的扩展。

三种引用机制:

引用符 描述
转义字符 \ 保留其后下一个字符的字面意义
单引号 '' 保留引用中所有字符的字面意义
双引号 "" 保留引用中所有字符的字面意义,例外的情况是 $, `, 和 \

单引号与双引号的使用区别:

quoting

注意,反引号 ```与单引号 '' 和双引号 "" 作用不同,是用于命令替换(Command Substitution),详见《Shell 常用扩展总结》。

注释(Comments)

# 起始的词使得这个词和所有同一行上所有剩余的字符都被忽略。

Shell 参数(Shell Parameters)

*参数(Parameter)*是存储值的实体。它可以是以下三类:

  • 变量
  • 位置参数
  • 特殊参数

变量(Varialbe)

变量,即用名称(name)表示的参数,其具有值(value)以及零或多个属性(attributes)

  • 通过 $name 引用,在双引号 "" 中可以被引用。
  • 通过以下语句为变量赋值:name=[value]。如果变量未赋值,默认值为 null 字符串。
  • 通过内建命令 unset 为取消变量。
  • 通过内建命令 declare 为变量分配属性(attributes)

所有值都接受以下扩展:

  • tilde expansion
  • parameter and variable expansion
  • command substitution
  • arithmetic expansion
  • quote removal

位置参数(Positional Parameters)

$n$1 表示第一个参数,$2 表示第二个参数,以此类推。

特殊参数(Special Parameters)

$0:表示脚本文件名

$#:表示命令行参数的个数

$?:前一个命令或函数的返回码,0 为成功,非 0 为错误/失败

$*:以”参数1 参数2 … “ 的形式保存所有参数

$@:以”参数1” “参数2” … 的形式保存所有参数

$$:本程序的 PID(进程 ID 号)

$!:最近执行的后台(即异步)命令的 PID

Shell 命令

简单命令(Simple Commands)

即单个命令。

管道(Pipelines)

pipeline(管道)是一个或多个命令的序列,用字符 | 分隔。管道的命令格式如下:

command [ | command2 ... ]

管道的特点如下:

  • 管道是一个由“标准输入输出”链接起来的进程集合;
  • 管道中的每个命令都作为单独的进程来执行(即在一个子 shell 中启动);
  • 每一个进程的输出(stdout)被直接作为下一个进程的输入(stdin);
  • 管道命令不处理 standard error output(stderr);
  • 管道的符号为:|

管道的处理流程如下图:

pipe

重定向(Redirection)

在命令执行前,它的输入和输出可能被 redirected (重定向),该功能可以用于如下场景:

  • 屏幕输出的信息很重要,而且需要将它存下来时;
  • 一些运行命令的可能已知错误信息,想以 2> /dev/null 将它丢掉;
  • 一些系统的例行命令(例如写在 /etc/crontab)的运行结果,需要存下来时;
  • 错误信息与正确信息需要分别输出时。

例子:

快速创建带内容的文件

$ echo "hello world" > /tmp/file
$ cat /tmp/file

将已知错误信息丢弃

$ find /tmp/ -name 2> /dev/null

描述符(Descriptor Number)

描述符 描述
0 标准输入(stdin)
1 标准输出(stdout)
2 标准错误输出(stderr)

操作符(Operator)

操作符 描述
< 重定向输入(Redirecting Input)
> 重定向输出(Redirecting Output),与 1> 等价
>> 追加到重定向输出(Appending Redirected Output)
2> 重定向错误输出(Redirecting Error)
2>> 追加到重定向错误输出(Appending Redirected Error)
&> 重定向标准输出和标准错误输出(Redirecting Standard Output and Standard Error)。 推荐使用,它与 >word 2>&1 在语义上等价
>& 同上,但不推荐使用
2>&1 将标准错误输出重定向到标准输出

序列(Lists of Commands)

list(序列)是一个或多个管道,用操作符 ;&&&|| 分隔的序列, 并且可以选择用 ;&<newline> 新行符结束。

操作符 例子 描述
&& command1 && command2 一个 AND 序列。command2 只有在 command1 返回 0 时才被执行
|| command1 || command2 一个 OR 序列。command2 只有在 command1 返回非 0 状态时才被执行
; command1; command2 结束一个序列。不考虑命令的退出状态,连续执行命令
<newline> command 结束一个序列
& command1 & 如果一个命令是由 & 结束的, shell 将在后台的子 shell 中执行这个命令
  • AND 和 OR 序列的返回状态是序列中最后执行的命令的返回状态。
  • 这些序列操作符中, &&|| 优先级相同, ;& 优先级相同。

退出状态(Exit Status)

  • 从 shell 的角度看,一个命令退出状态是 0 意味着成功退出。 非零状态值表明失败。
  • 如果没有找到命令,为执行它而创建的子进程返回 127。
  • 如果找到了命令但是文件不可执行,返回状态是 126。
  • 如果命令由于扩展或重定向错误而失败,退出状态大于零。

使用场景

在某些情况下,很多命令我想要一次输入去运行,有两种方法:

  1. Shell Script
  2. 使用序列

例如,一串无人值守源代码形式安装的命令如下:

$ ./configure && make && make install

复合命令(Compound Commands)

compound command(复合命令)是如下情况之一:

  • (list)

  • { list; }

  • ((expression))

  • [[ expression ]]

  • if list; then list; [ elif list; then list; ] ... [ else list; ] fi

  • case word in [ [(] pattern [ | pattern ]

  • while list; do list; done

  • until list; do list; done

  • for name [ in word ] ; do list ; done

    #!/bin/bash
    
    # 声明一个数组变量
    order_array=(
      10000
      10001
      10002
    )
    
    # 循环打印与保存到文件
    for id in ${order_array[@]}
    do
      echo "order is $id" | tee -a result.txt
    done
    
  • for (( expr1 ; expr2 ; expr3 )) ; do list ; done

  • select name [ in word ] ; do list ; done


GUN/Bash 提供了一些内建命令 (BUILTIN COMMANDS),用于在命令行上方便使用:

常用类

echo 显示一行文本或变量

unset 取消变量

set 查看所有变量(环境变量&用户变量)

env 查看所有环境变量(格式好看些)

export
  • 查看所有环境变量
  • 将局部变量转成环境变量:
    • 可以利用 export 命令将局部变量转为环境变量,但是用户注销时值将丢失;
    • 环境配置文件中,经常会用到 export 命令,相当于每次登录时系统都帮用户 export 一下所需环境变量;
    • 环境变量在当前进程 fork 出来的子进程中也能被访问到;
    • 目前发现安装软件时有用。
source` 或 `.
  • 加载环境配置文件(无须 exit 注销)
  • 执行脚本(在父进程bash中执行,设置的变量都会保留)
declare` 或 `typeset
  • -a 定义数组类型
  • -i 定义整数类型
  • -x 将用户变量转成环境变量(与 export 一样)
  • +x 将环境变量降为用户变量
  • -r 定义 readonly 类型

read 读取来自键盘输入的变量

  • -p 后接提示符
  • -t 后接等待“秒数”
test
  • -e 该文件名是否存在(exist)
  • -s 该文件大小是否非 0
  • -z 是否为空字符串(zero)
  • -f 是否为文件(file)
  • -d 是否为目录(directory)
  • -b 是否为块特殊文件(block)
  • -L 是否为连接文件(link)
  • -r -w -x 是否可读、可写、可执行
  • -a -o ! 且、或、非
  • -eq -ne -gt -lt -ge -le (判断2个整数)相等、不等、大于、小于、大于等于、小于等于
[]
  • 中括号 [] 的使用方法与 test 命令几乎一模一样,只是中括号常用于条件判断式 if…then…fi
  • 中括号内的每个元素,都要有空格符分隔
  • 中括号内的变量,最好都以双引号括起来
  • 中括号内的常量(字符串),最好都以单引号 '' 或双引号 "" 括起来

shsh 方式执行,至少需要 r 权限;若以绝对路径方式执行,则需要 rx 权限

  • -n 不执行 script,仅验证语法。若语法无误,则不显示任何信息。(貌似仅能验证关键字错误?)
  • -v 在执行 script 前,先将 script 的内容输出到屏幕上
  • -x 将 script 执行过程逐步输出到屏幕上

作业控制类

Bash 是一个多任务的 CLI ,有以下作业控制(Job Control)相关的命令:

命令 描述
jobs 显示(当前会话中的)后台作业表
fg 将后台作业调到前台执行(前台运行作业)
bg 继续执行指定的后台作业(后台运行作业)
Ctrl+Z 暂停/挂起目前的命令,转入后台运行。通过在命令后追加一个&,可以将该命令转入后台运行
Ctrl+C 终止目前的命令

Shell 扩展(Shell Expansions)

命令行的扩展是在拆分成词之后进行的。共有七种类型的扩展:

  • brace expansion
  • tilde expansion
  • parameter and variable expansion
  • command substitution
  • arithmetic expansion
  • word splitting
  • filename expansion

常用的四种如下

花括号扩展(Brace Expansion)

用于偷懒,例如:

x{a,b,cd}y

扩展为:

xay xby xcdy

再例如:

mkdir /usr/local/src/bash/{old,new,dist}

扩展为:

mkdir /usr/local/src/bash/old
mkdir /usr/local/src/bash/new
mkdir /usr/local/src/bash/dist

参数和变量扩展(Parameter and Variable Expansion)

${parameter}

算术扩展(Arithmetic Expansion)

$(( expression ))

命令替换(Command Substitution)

命令替换允许命令的输出替换命令本身。当命令被以下特殊字符包括时,将发生命令替换:

$(command)

`command`

默认提示符

命令提示符涉及到以下两个环境变量:

环境变量 描述
PS1 主提示符,Bash 会在准备好读入一条命令时显示,默认值 \s-\v\$
PS2 次提示符,Bash 会在需要更多的输入来完成一条命令时显示,默认值 >

定制提示符

Bash 允许通过插入一些反斜杠转义的特殊字符来定制这些提示符,常用的转义字符如下:

转义字符 描述
\h 主机名,第一个 . 之前的部分
\H 主机名
\j shell 当前管理的作业数量
\l shell 的终端设备名的基本部分
\n 新行符
\r 回车
\s shell 的名称, $0 的基本部分 (最后一个斜杠后面的部分)
\u 当前用户的用户名
\v bash 的版本 (例如,4.3)
\w 当前工作目录
\W 当前工作目录的基本部分
\! 此命令的历史编号
\# 此命令的命令编号
\$ 如果有效 UID 为 0,则显示 #, 否则 $
\\ 一个反斜杠

除此之外,还有一些不太常用的日期转义字符:

转义字符 描述
\d 当前日期,格式是 “星期 月份 日” (例如,”Tue May 26”)
\D{format} 自定义日期格式,花括号是必需的
\t 当前时间,采用 24 小时制的 HH:MM:SS 格式
\T 当前时间,采用 12 小时制的 HH:MM:SS 格式
\@ 当前时间,采用 12 小时制上午/下午 am/pm 格式
\A 当前时间,采用 24 小时制上午/下午格式

如何定制

由于 PS1 默认设置的 \s-\v\$ 实在是太废毫无信息量可言,显示如下:

bash-4.3$

因此可以通过修改 ~/.bash_profile 文件来定制自己的命令提示符。例如,使用 CentOS 默认设置的 [\u@\h \W]\$

$ vim ~/.bash_profile

export PS1="[\u@\h \w]\$ "

定制后,能够知道当前用户、主机名、工作目录:

[root@BGP-BJ-C-5HL ~]$

下面总结的是 GNU/Bash 中部分最实用的命令名称及默认情况下它们关联的快捷键。更多快捷键请参考 GNU/Bash 文档的 Readline 库。

移动(Commands for Moving)

Shortcut Command name Description
Ctrl + A beginning-of-line Move to the start of the current line.
Ctrl + E end-of-line Move to the end of the line.
Ctrl + F forward-char Move forward a character.
Ctrl + B backward-char Move back a character.
Alt + F forward-word Move forward to the end of the next word. Words are composed of alphanumeric characters (letters and digits).
Alt + B backward-word Move back to the start of the current or previous word. Words are composed of alphanumeric characters (letters and digits).
Ctrl + L clear-screen Clear the screen leaving the current line at the top of the screen. With an argument, refresh the current line without clearing the screen.

操纵历史行(Commands for Manipulating the History)

Shortcut Command name Description
Ctrl + P previous-history Fetch the previous command from the history list, moving back in the list.
Ctrl + N next-history Fetch the next command from the history list, moving forward in the list.
Ctrl + R reverse-search-history Search backward starting at the current line and moving `up’ through the history as necessary. This is an incremental search.
Alt + Ctrl + Y yank-nth-arg Insert the first argument to the previous command (usually the second word on the previous line) at point. With an argument n, insert the nth word from the previous command (the words in the previous command begin with word 0). A negative argument inserts the nth word from the end of the previous command. Once the argument n is computed, the argument is extracted as if the “!n” history expansion had been specified.

改变文本(Commands for Changing Text)

Shortcut Command name Description
Ctrl + D delete-char Delete the character at point. If point is at the beginning of the line, there are no characters in the line, and the last character typed was not bound to delete-char, then return EOF.

剪切和粘贴(Killing and Yanking)

Shortcut Command name Description
Ctrl + K kill-line Kill the text from point to the end of the line.
Ctrl + U backward-kill-line Kill backward from point to the beginning of the line.
Alt + D kill-word Kill from point to the end of the current word, or if between words, to the end of the next word. Word boundaries are the same as those used by forward-word.
Ctrl + W backward-kill-word Kill the word behind point, using white space as a word boundary. The killed text is saved on the kill-ring.
Ctrl + Y yank Yank the top of the kill ring into the buffer at point.

补全(Completing)

Shortcut Command name Description
TAB complete Attempt to perform completion on the text before point. Bash attempts completion treating the text as a variable (if the text begins with $), username (if the text begins with ~), hostname (if the text begins with @), or command (including aliases and functions) in turn. If none of these produces a match, filename completion is attempted.

猜你喜欢

转载自blog.csdn.net/younow22/article/details/113445410