Linux & Shell
Last updated
Was this helpful?
Last updated
Was this helpful?
注意:本节课讲的都是 bash 命令。
Bash 权威资料:
当我们在输入命令时,机器会依据 $PATH
变量中存储的目录依次查找,直到找到对应的命令,之后执行命令。如果找不到命令,则会输出报错信息。
注意:当前目录一般不被包含在搜索路径中,因此需要使用类似于 ./custom_command args
的方式运行当前路径下的命令,而不能使用 custom_command args
这种写法。
echo 命令的作用是向输出流输出东西
简单理解:文件属于流,标准输入流与标准输出流是特殊的流
重定向
管道
后一条命令所需的输入流为前一条命令执行后的输出流
注意区分输入、输出、返回(见下一小节)的概念。管道是针对输入与输出的,不是针对返回的。
shell 命令一般将输出(output)写入到标准输出流中,将错误信息(errors)写入到标准错误流中。另外,每条命令执行结束后会返回一个返回状态码(Return Code),返回状态码为 0 表示正常运行,非零表示存在错误。注意:状态码的作用是为了方便脚本与命令之间的通信,而输出与错误信息是为了方便用户。
输出与错误信息可以用于重定向或者管道操作。
状态码的用途举例:
在布尔操作(例如:&&
和 ||
,注意它们都是短路求值的)被使用到,false
命令的返回状态码为 1,true
命令的返回状态码为 0。
使用特殊变量 $?
获取上一条命令的返回状态码
命令的形式
一般而言,命令的形式为 命令名 参数列表
形式。某些命令还需要接收流或是文件里的数据,例如:直接执行 grep pattern
命令时,会要求继续输入,直至按下 Ctrl+Z
快捷键结束,对于这类命令,一般要使用管道,最常见的例子是:
注意: 一条命令的输入可能包含“参数列表”或者“流”, 而执行这条命令的效果可能会向“流”输出, 而执行完命令后会有一个状态码返回, 状态码返回为 0 则表示命令正常执行. 而管道 |
的作用是将上一条
直接将字符串作为命令的标准输入可以使用 <<<
:
引号
在 bash 命令中,很多时候引号不是必须的。且单引号与双引号的效果是不一样的
空格
空格是很重要的分隔符,因此向命令传递带有空格的参数时,要用单引号或双引号将该参数包裹起来,或者使用 \
(反斜线空格)的形式进行转义,例如:
再例如,在 bash 中定义变量时,等号前后不能有空格:
备注:空格需要特别小心,很容易出错,需仔细检查。
寻求命令的帮助
寻求命令的帮助可以使用 命令名 --help
,退出帮助文档的快捷键为 q
。
第一项的第一个字母代表类型,其中:d
代表目录,-
代表文件。后面九个字母分为三组,依次代表所有者、用户组、其他人的权限,包括读(r
)、写(w
)、可执行(x
)三种。对于目录来说:
读权限代表可以查看目录下的文件列表。即可以对目录使用 ls
命令;
写权限代表可以:在目录下创建子目录或文件;删除目录下的子目录或文件(无论子目录或文件的权限是什么);修改子目录或文件的文件名;移动子目录或文件位置。即可以对该目录下的东西使用 rm
,mkdir
,mv
等命令;
可执行权限代表可以进入该目录。即可以 cd
进入该目录;
第二项代表连接数
第三项代表文件所属用户
第四项代表文件所属群组
第五项代表文件大小,单位为字节,对于文件夹,其大小不是指该文件夹下的所有文件的总大小
第六项为文件最后修改时间
第七项为文件名
变量可以分为 shell 变量与环境变量。环境变量是从父进程中继承过来的,如果当前进程产生了子进程,则子进程将会继承所有的环境变量。shell 变量只在当前进程中起作用,不会发生继承关系。
列出当前 shell 的环境变量:
添加环境变量:
IFS (internal field separator,内部字段分隔符)环境变量默认为空格,制表符,换行符。
** ${varname:-"abc"}
**
这种语法表示给变量设定缺省值,若 varname
未被定义,则 varname
将被赋值为 abc
,否则该条语句不起作用。例如:
已经被定义的情形
没有被定义的情形
实例:在 Github Pytorch 项目的 README 文档介绍如何进行源码安装时,需要执行如下命令
** ${!arr[@]}
,${!arr@}
,${!var}
**
${!arr[@]}
用于返回数组下标,例如:
${!arr@}
表示输出以"arr"开头的变量名,例如:
${!var}
表示取出以var变量的值命名的变量的值(类似于C语言中的指针),例如:
** 字符串替换:`` **
${parameter/pattern/string}
表示将 parameter
变量中第一次出现的 pattern
替换为 string
,例如:
${parameter/pattern/string}
表示全部替换,例如:
获取文件扩展名
实例
输入:
输出:
<<<
:
<<
:
<
:
>>
:
>
:
|
: 管道
&
: 后台运行
&&
: 前面的命令执行成功(返回值是0)时才执行后面的命令
||
: 前面的命令执行失败(返回值非0)时才执行后面的命令
(...)
: 子 shell
>&
和 <&
<>
: 读写文件打开
-
: 不是通用符号, 有些命令会使用 -
作为参数表示从标准输入读取数据
$(...)
: 命令替换
<(...)
: 进程替换
[[ ... ]]
: 条件测试
此命令的作用是将命令 ls data.txt
运行过程中产生的标准错误信息忽略,而将产生的标准输出信息重定向(写入)至 log.txt
中。具体解释如下:
文件描述符
文件描述符是与文件输入、输出关联的整数。它们用来跟踪已打开的文件。最常见的文件描述符是stdin、stdout、和stderr。我们可以将某个文件描述符的内容重定向到另外一个文件描述符中。《linux shell脚本攻略》
具体来说,常见的文件描述符为 0、1、2 这三个,分别对应 stdin(标准输入)、stdout(标准输出)、stderr(标准错误)。事实上:
stdin 对应于 /dev/stdin
stdout 对应于 /dev/stdout
stderr 对应于 /dev/stderr
在 shell 命令或脚本中常用的是 1 和 2。因此在上面的例子中,是将命令 ls data.txt
产生的标准输出重定向至 log.txt
中,将产生的标准错误信息重定向至 /dev/null
中。
/dev/null
/dev/null
是 linux 中的一个特殊设备,作用是接受输入并丢弃。在 shell 命令中的作用一般是用来接受输出信息,避免在屏幕上显示,同时也不希望使用文件对输出进行接收。
一般用户的命令提示符为 $
,而 root 用户的命令提示符为 #
。
Ctrl+L
快捷键用于清除屏幕
/proc 目录
/proc
目录中按进程 id 存放着进程的相关信息,特别地,上述命令用于查看该进程运行时的环境变量。
单引号的用于忽略所有特殊字符的特殊含义,双引号忽略大多数特殊字符,但不会忽略 $
、\
、反引号。反引号的作用是命令替换
命令替换(command substitution)的写法如下:
其运行逻辑是将 date
视为一条命令执行,将输出替换掉 `date`
。
备注:
反引号的写法在任何类型的 shell 中都是通用的,但 $(CMD)
的写法只在 bash 中有效。但 $()
允许嵌套,而反引号的写法不允许嵌套。
如果被替换的命令输出有多行或者有连续的多个空格,那么使用 echo 命令进行输出时,若不用双引号将变量名包裹起来,那么换行符以及连续的空格将会被替换为系统默认的空白符,例如:
进程替换(process substitution)的写法如下:
其运行逻辑是:运行 ls a
,将结果存入一个临时文件,并用临时文件名替换掉 <(ls a)
,也就是相当于:
&&
、||
、;
这三者统称为 list operators for separating shell commands
假定 script.sh
的内容如下:
赋予执行权限
读、写、运行的权限分别为 4、2、1,chmod
命令的三个数字依次代表拥有者、用户组、其他人的权限,权限数字为三项权限之和。因此 744
代表拥有者具有读、写、运行权限,用户组具有读权限,其他人具有读权限。
运行
第一种运行方式为:解释程序名+脚本名
,这种方式下当前用户对脚本不需要有可执行权限。
第二种运行方式为:脚本名
,这种方式下当前用户对脚本必须有可执行权限。
注意使用这种运行方式时,解释程序由第一行的 #!/bin/bash
决定,这一特殊行被称为 shebang 行。
因此,对于 python 脚本来说,也可以使用第二种方式运行。shebang 行的最佳实践写法是:
调试
可以使用 bash -x 脚本名
的方式进行调试,也可以使用类似下面的方式进行自定义调试:
注意点:
定义变量或者给变量赋值时,等号的左右两端不能有空格
使用算术运算的形式为:$[var1+var2]
使用命令的执行结果为变量赋值
变量的值可以都是字符串
引用变量的形式为:$var
或者 ${var}
,两种写法是一样的,为了避免出错,建议使用后者
shell 脚本与其他脚本的特殊之处在于 shell 脚本中有许多特殊的预设变量
$0
:执行脚本名(不算做脚本参数)。注意:在调用函数时,指的是 函数名.sh
$1
- $9
:脚本的参数,第 1-9 个参数。注意:在调用函数时,指的是函数的参数
$@
:脚本的所有参数(不包括 $0
)。注意:在调用函数时,指的是函数的所有参数
$#
:脚本的参数个数(不包括 $0
)
$?
:前一条命令的返回值(上一条命令如果正确执行了,返回值为 0,否则不为 0)
$$
:当前脚本的进程号
!!
:完整的上一条命令
$_
:上一条命令的最后一个参数
${@:2}
:指的是第 $2
至之后所有的参数(包含 $2
),此语法只在用 bash 执行时有效,使用 sh 执行时会报错
数组的定义与使用的方式如下
bash 4.0 以后,引入了关联数组,即:字典。
列出所有元素/索引
test 命令
test 的作用是检测某个条件是否成立,例如:
需要注意的是,test
命令没有输出,当条件成立时,返回状态码为 0;条件不成立时,返回状态不是 0。
test 命令还有一种“语法糖”的形式更为常见,注意左右中括号的空格是不能少的:
条件语句
类似于 [[ -f $file ]]
这种写法:
[ -a FILE ]
: 如果 FILE 存在则为真
[ -d FILE ]
: 如果 FILE 存在且为目录则为真
[ -r FILE ]
: 如果 FILE 存在且为可读文件则为真
[ -w FILE ]
: 如果 FILE 存在且为可写文件则为真
[ -x FILE ]
: 如果 FILE 存在且为可执行文件则为真
[ -z STRING]
: 如果 STRING 的长度为 0 为真
[ -n STRING]
: 如果 STRING 的长度大于 0 为真
[ STRING ]
: 字符串不空为真, 类似于 -n
[ INT1 -le INT2 ]
: 数值上 INT1 小于 INT2 为真
[ ! EXPR ]
: 非
[ EXPR1 -a EXPR2]
: 与
[ EXPR1 ] && [ EXPR2 ]
: 与
[ EXPR1 -o EXPR2]
: 或
[ EXPR1 ] || [ EXPR2 ]
: 或
备注:双圆括号一般用于数值比较,写法上更方便,例如:[ 1 -le 2 ]
等价于 (( 1<2 ))
;双中括号有时也是为写法的便利,例如:[[ $a != 1 || $b = 2 ]]
这种写法等价于 [ $a != 1 ] || [ $b = 2 ]
,注意前者必须用双中括号而不能用单中括号。另外,注意要在中括号内部的左右两边各留一个空格。
函数定义
函数使用
set -e
表示 shell 脚本遇到出错的命令就立刻中止脚本运行(备注:存在一些例外情况,见前面的链接或man手册),如果在设置了需要允许某行命令报错也能正常执行,则可以使用 <error-command> || true
或者 <error-command> || :
来使得命令出错时也能继续运行。
set -u
表示 shell 脚本在使用未定义的变量时就报错并中止运行
set -o
表示打开特殊选项,对应地,set +o
表示关闭特殊选项,此处 set -o pipefail
是针对 shell 的一个诡异行为(set -e
的例外情况):在使用管道时,只有在管道的最后一条命令出错时,才认为整条命令出错。而使用了 set -o pipefail
表示在管道的任意位置出错则报错
set -x
表示执行时会打印出脚本中的每条语句,并且变量名将被替换为其实际内容(debug 时推荐使用)
一般来说,可以使用 ctrl+c
来中断程序,又或者使用 kill -9 <pid>
杀死进程。这些操作本质上是在给进程发信号(signal)。每个进程在运行中如果接收到信号,那么它必须处理这些信号。而 kill
命令的作用就是给进程发信号。
SIGHUP
1
SIGINT
ctrl+c
终止程序运行
2
SIGKILL
杀死进程
9
SIGSTOP
ctrl+z
暂停进程运行
19
SIGCONT
继续进程运行
18
SIGTERM
终止程序运行
15
SIGQUIT
ctrl+\
终止程序运行,且终止程序前会进行 core dump
3
备注:
core dump 又叫核心转储,当程序运行过程中发生异常,程序异常退出时,由操作系统把程序当前的内存状况存储在一个 core 文件中。但是否会生成该文件还需要打开一项设定:
某些信号允许程序自己定义如何进行处理,例如:
使用 ctrl+c
发送 SIGINT
无法中断程序
nohup
、&
、jobs
命令nohup:在关闭终端时,会对所有由该终端运行的进程发送 SIGHUP
信号,默认情况下,所有进程均会退出。但使用了 nohup 启动命令后,该程序将无法得到 stdin 的输入,并且将 stderr 与 stdout 重定向到 nohup.out 文件中。并且在关闭终端时,该进程不会接受到 SIGHUP
信号,也就不会终止。
&:表示将程序放在后台运行(这种进程可由 jobs 命令查看到),输出进程 ID。终端可继续执行其他命令。然而,这种进程依然会对终端进行输出,这可以通过重定向来避免。例如:
备注:这里的 > jupyter.log 2>&1
即为重定向,其中 > jupyter.log
是 1 > jupyter.log
的简写,表示将标准输出(stdout)重定向至 jupyter.log
文件中,而 2>&1
表示将标准错误(stderr)重定向至标准输出中,而前者已被重定向,因此全部被重定向至 jupyter.log
中。此处的 2>&1
中的 &
如果不写,则表示将标准输出重定向到一个名为 1
的文件中
jobs:jobs
命令用于查看当前终端放入后台运行的进程的运行情况。
SIGINT
、SIGTERM
、SIGQUIT
、SIGKILL
:ctrl+b
+ alt+方向键
:控制面板大小
ctrl+b
+ z
:将当前面板扩大到整个窗口(或退出这一模式)
按下 ctrl+b
后,按下 :
进入底线命令模式
说明:
默认的 PATH
环境变量的值通常情况下为
其中,bin
与 sbin
的区别在于前者适用于所有用户,后者适用于普通用户。而 /bin
目录存放的应该是系统所必须的命令或软件,/usr/bin
存放的是非系统必须的软件,一般而言,Linux 发行版的“软件商店”(例如 Ubuntu 的apt)里的软件会安装在此处。手动编译或者通过其他渠道获取的适用于所有用户的软件一般放在 /usr/local/bin
目录下。
这里主要涉及到 shell 的两个属性: login/non-login (是否需要登录); interactive/non-interactive (是否允许交互)
注意: non-login shell 只能在 login shell 中才能打开
备注:各种类型的 shell 也可以在 /etc/passwd
文件中可见一斑:
对于 login interactive/non-interactive shell,则登陆时首先查看 /etc/profile
是否存在并执行该文件, 接下来, 按顺序依次查找 ~/.bash_profile
,~/.bash_login
,~/.profile
这三个文件是否存在并且有可读权限,只执行找到的第一个则停止。
对于 non-login interactive shell,依次执行 /etc/bash.bashrc
(ubuntu 为/etc/bash.bashrc
,Red Hat 为 /etc/bashrc
) 以及 ~/.bashrc
。
对于 non-login non-interactive shell, 不会执行任何配置文件
备注:
(1)一般而言,~/.bash_profile
里会包含这种语句
(2)~/.bash_profile
仅仅适用于 bash shell,而 ~/.profile
适用于所有的 shell。
备注:bash 的 manpage 中节选如下:
推荐的配置文件里写的内容:
做法一: 这种大概是最为合理的做法, 但维护起来比较困难, 适用于需要支持多种 Shell 环境(如登录 Shell 和非登录 Shell)的场景。
(1) 无须使用 ~/.bash_login
文件
(2) 在 ~/.bash_profile
文件中包含如下内容即可
(3) 在 ~/.profile
文件中配置与交互无关的内容, 例如: 环境变量
(4) 在 ~/.bashrc
中设置与交互式 Shell 相关的配置, 例如: 别名, PS1 变量
(5) ~/.profile
与 ~/.bashrc
不要去相互引用
做法二(大多数情况下使用这种即可): 简单, 适用于交互式 shell
(1) 无须使用 ~/.bash_login
, ~/.bash_profile
文件
(2) 在 ~/.bashrc
里配置别名, PS1变量, 环境变量等
(3) 在 ~/.profile
里包含如下内容即可:
安装
使用
按上下键在当前目录浏览,enter键进入,esc键退出文件。q键退出ranger
将按键显示在屏幕上
windows 上传文件中文乱码
Commands will often return output using STDOUT
, errors through STDERR
, and a Return Code to report errors in a more script-friendly manner. The return code or exit status is the way scripts/commands have to communicate how execution went. A value of 0 usually means everything went OK; anything different from 0 means an error occurred.
推荐链接:
参考
主要参考
set
命令用于改变 shell 脚本的一些执行逻辑,一般写在脚本的开头。例如在默认情况下,遇到未定义的变量名,shell脚本不会报错,而是使用空字符串代替,为此,可以在脚本的最开头设置:set -u
,使得当在使用未定义的变量时将会直接报错,并退出脚本。类似地,在 bats
测试中(参考):
完整表格可参见
参考,大略意思如下:四者都可用于终止程序运行,SIGKILL
是结束进程的强制手段,程序不能改变接受到此信号的行为。而其余三者都可以由程序决定如何处置这些信号。默认行为下,推荐使用 SIGTERM
结束进程。
Linux 发行版的目录结构由 所规定,以 Ubuntu 18.04 为例,大致如下:
,,,