Linux & Shell
第 1 课:shell 命令
注意:本节课讲的都是 bash 命令。
Bash 权威资料: https://www.gnu.org/software/bash/manual/bash.html
shell 命令的一般性介绍
机器上有哪些 shell 命令?
当我们在输入命令时,机器会依据 $PATH
变量中存储的目录依次查找,直到找到对应的命令,之后执行命令。如果找不到命令,则会输出报错信息。
注意:当前目录一般不被包含在搜索路径中,因此需要使用类似于 ./custom_command args
的方式运行当前路径下的命令,而不能使用 custom_command args
这种写法。
echo 命令
echo 命令的作用是向输出流输出东西
流(输入、输出)
简单理解:文件属于流,标准输入流与标准输出流是特殊的流
重定向
管道
后一条命令所需的输入流为前一条命令执行后的输出流
注意区分输入、输出、返回(见下一小节)的概念。管道是针对输入与输出的,不是针对返回的。
shell 命令的“返回”
Commands will often return output using
STDOUT
, errors throughSTDERR
, 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. Shell Tools and Scripting · the missing semester of your cs education (mit.edu)
shell 命令一般将输出(output)写入到标准输出流中,将错误信息(errors)写入到标准错误流中。另外,每条命令执行结束后会返回一个返回状态码(Return Code),返回状态码为 0 表示正常运行,非零表示存在错误。注意:状态码的作用是为了方便脚本与命令之间的通信,而输出与错误信息是为了方便用户。
输出与错误信息可以用于重定向或者管道操作。
状态码的用途举例:
在布尔操作(例如:
&&
和||
,注意它们都是短路求值的)被使用到,false
命令的返回状态码为 1,true
命令的返回状态码为 0。使用特殊变量
$?
获取上一条命令的返回状态码
shell 命令形式的一般性说明
命令的形式
一般而言,命令的形式为 命令名 参数列表
形式。某些命令还需要接收流或是文件里的数据,例如:直接执行 grep pattern
命令时,会要求继续输入,直至按下 Ctrl+Z
快捷键结束,对于这类命令,一般要使用管道,最常见的例子是:
注意: 一条命令的输入可能包含“参数列表”或者“流”, 而执行这条命令的效果可能会向“流”输出, 而执行完命令后会有一个状态码返回, 状态码返回为 0 则表示命令正常执行. 而管道 |
的作用是将上一条
直接将字符串作为命令的标准输入可以使用 <<<
:
引号
在 bash 命令中,很多时候引号不是必须的。且单引号与双引号的效果是不一样的
空格
空格是很重要的分隔符,因此向命令传递带有空格的参数时,要用单引号或双引号将该参数包裹起来,或者使用 \
(反斜线空格)的形式进行转义,例如:
再例如,在 bash 中定义变量时,等号前后不能有空格:
备注:空格需要特别小心,很容易出错,需仔细检查。
寻求命令的帮助
寻求命令的帮助可以使用 命令名 --help
,退出帮助文档的快捷键为 q
。
shell 基础命令
目录及文件的权限含义
推荐链接:鸟哥私房菜
第一项的第一个字母代表类型,其中:d
代表目录,-
代表文件。后面九个字母分为三组,依次代表所有者、用户组、其他人的权限,包括读(r
)、写(w
)、可执行(x
)三种。对于目录来说:
读权限代表可以查看目录下的文件列表。即可以对目录使用
ls
命令;写权限代表可以:在目录下创建子目录或文件;删除目录下的子目录或文件(无论子目录或文件的权限是什么);修改子目录或文件的文件名;移动子目录或文件位置。即可以对该目录下的东西使用
rm
,mkdir
,mv
等命令;可执行权限代表可以进入该目录。即可以
cd
进入该目录;
第二项代表连接数
第三项代表文件所属用户
第四项代表文件所属群组
第五项代表文件大小,单位为字节,对于文件夹,其大小不是指该文件夹下的所有文件的总大小
第六项为文件最后修改时间
第七项为文件名
环境变量
变量可以分为 shell 变量与环境变量。环境变量是从父进程中继承过来的,如果当前进程产生了子进程,则子进程将会继承所有的环境变量。shell 变量只在当前进程中起作用,不会发生继承关系。
列出当前 shell 的环境变量:
添加环境变量:
IFS(并不是环境变量)
IFS (internal field separator,内部字段分隔符)环境变量默认为空格,制表符,换行符。
Shell-Parameter-Expansion
** ${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}
表示全部替换,例如:
获取文件扩展名
实例
输入:
输出:
Bash Special Characters (TODO)
https://tldp.org/LDP/abs/html/special-chars.html
<<<
:
<<
:
<
:
>>
:
>
:
|
: 管道
&
: 后台运行
&&
: 前面的命令执行成功(返回值是0)时才执行后面的命令
||
: 前面的命令执行失败(返回值非0)时才执行后面的命令
(...)
: 子 shell
>&
和 <&
<>
: 读写文件打开
-
: 不是通用符号, 有些命令会使用 -
作为参数表示从标准输入读取数据
$(...)
: 命令替换
<(...)
: 进程替换
[[ ... ]]
: 条件测试
/dev/null、文件描述符
主要参考知乎问答
此命令的作用是将命令 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
快捷键用于清除屏幕
linux系统目录
/proc 目录
/proc
目录中按进程 id 存放着进程的相关信息,特别地,上述命令用于查看该进程运行时的环境变量。
反引号、单引号、双引号
单引号的用于忽略所有特殊字符的特殊含义,双引号忽略大多数特殊字符,但不会忽略 $
、\
、反引号。反引号的作用是命令替换
进程替换与命令替换
命令替换(command substitution)的写法如下:
其运行逻辑是将 date
视为一条命令执行,将输出替换掉 `date`
。
备注:
反引号的写法在任何类型的 shell 中都是通用的,但
$(CMD)
的写法只在 bash 中有效。但$()
允许嵌套,而反引号的写法不允许嵌套。如果被替换的命令输出有多行或者有连续的多个空格,那么使用 echo 命令进行输出时,若不用双引号将变量名包裹起来,那么换行符以及连续的空格将会被替换为系统默认的空白符,例如:
进程替换(process substitution)的写法如下:
其运行逻辑是:运行 ls a
,将结果存入一个临时文件,并用临时文件名替换掉 <(ls a)
,也就是相当于:
&&
、||
、;
&&
、||
、;
这三者统称为 list operators for separating shell commands
第 2 课:shell 脚本
怎么运行脚本
假定 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
set
命令用于改变 shell 脚本的一些执行逻辑,一般写在脚本的开头。例如在默认情况下,遇到未定义的变量名,shell脚本不会报错,而是使用空字符串代替,为此,可以在脚本的最开头设置:set -u
,使得当在使用未定义的变量时将会直接报错,并退出脚本。类似地,在 bats
测试中(参考博客):
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 时推荐使用)
第 5 课:命令行环境
5.1 Job Control
Signal
一般来说,可以使用 ctrl+c
来中断程序,又或者使用 kill -9 <pid>
杀死进程。这些操作本质上是在给进程发信号(signal)。每个进程在运行中如果接收到信号,那么它必须处理这些信号。而 kill
命令的作用就是给进程发信号。
完整表格可参见 man-pages
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
、&
、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
:
SIGINT
、SIGTERM
、SIGQUIT
、SIGKILL
:参考博客,大略意思如下:四者都可用于终止程序运行,SIGKILL
是结束进程的强制手段,程序不能改变接受到此信号的行为。而其余三者都可以由程序决定如何处置这些信号。默认行为下,推荐使用 SIGTERM
结束进程。
5.2 Terminal Multiplexers (tmux)
ctrl+b
+ alt+方向键
:控制面板大小
ctrl+b
+ z
:将当前面板扩大到整个窗口(或退出这一模式)
命令环境
按下 ctrl+b
后,按下 :
进入底线命令模式
5.3 Alias and Dotfiles
5.4 Remote Macheines
5.5 zsh
Linux
Linux 目录结构
Linux 发行版的目录结构由 Filesystem Hierarchy Standard 所规定,以 Ubuntu 18.04 为例,大致如下:
说明:
默认的
PATH
环境变量的值通常情况下为其中,
bin
与sbin
的区别在于前者适用于所有用户,后者适用于普通用户。而/bin
目录存放的应该是系统所必须的命令或软件,/usr/bin
存放的是非系统必须的软件,一般而言,Linux 发行版的“软件商店”(例如 Ubuntu 的apt)里的软件会安装在此处。手动编译或者通过其他渠道获取的适用于所有用户的软件一般放在/usr/local/bin
目录下。
管理用户相关
用户登录
各种类型的 shell
根据 链接4 中所指出的,
A shell can be interactive or non-interactive
所谓的 interactive shell 即为与用户进行交互的 shell。而 non-interactive shell 指的是不会与用户进行交互的 shell,例如在 shell 中执行
其执行逻辑是新开一个 non-interactive shell,在该 shell 中执行 run.sh
中的命令
进一步,interactive shell 又分为 interactive login shell 与 interactive non-login shell,例子是:使用 ssh
远程连接时得到的 shell,使用 --login
参数启动的 bash
是 login shell;而 non-login shell 则必须在 login shell 中才能打开,例如在一个 shell 中直接输入
那么将会进入一个全新的 non-login shell(虽然可能不容易察觉,但使用 ctrl+D
快捷键便可以退出这个 non-login shell,从而回到原来的 shell)。
备注:各种类型的 shell 也可以在 /etc/passwd
文件中可见一斑:
总结如下
non-interactive shell:不接受交互式操作的 shell;
interactive shell:交互式 shell;
login shell:需要登陆的shell,例如使用 ssh 登陆;
non-login shell:不需要登陆的shell,例如在一个交互式(login 或者 non-login)的 shell 中输入 bash 所得到的 shell。
shell 的配置文件
对于 login shell,则登陆时首先查看 /etc/profile
是否存在并执行该文件,接下来,按顺序依次查找 ~/.bash_profile
,~/.bash_login
,~/.profile
这三个文件是否存在并且有可读权限,只执行找到的第一个则停止。
对于 non-login shell,依次执行 /etc/bash.bashrc
(ubuntu 为/etc/bash.bashrc
,Red Hat 为 /etc/bashrc
) 以及 .bashrc
。
【存疑】:对于 non-interactive shell,以上所有的配置文件均不会被执行
备注:
(1)一般而言,~/.bash_profile
里会包含这种语句
(2)~/.bash_profile
仅仅适用于 bash shell,而 ~/.profile
适用于所有的 shell。
备注:bash 的 manpage 中节选如下:
Ubuntu
工具
ranger
安装
使用
按上下键在当前目录浏览,enter键进入,esc键退出文件。q键退出ranger
screenkey
将按键显示在屏幕上
zip
windows 上传文件中文乱码
Last updated