Shell Types
echo $SHELL#查看当前使用的是什么 shell
- Bourne Shell (sh):良好的输入/输出控制,常用于脚本;不适合交互式用户;默认提示为
$ - C Shell (csh):invented by Bill Joy;语法和 C 语言类似;输入输出不如 sh;对交互式用户更为友好;工作控制、历史记录等新功能;默认提示为
% - Bourne-Again Shell (bash):GNU 开发的自由软件;提供 csh 的所有交互式特征;编程语言和 sh 兼容;默认提示是
$,root是#
Wild card(通配符)
| 符号 | 名称 | 作用 | 匹配机制 | 示例 |
|---|---|---|---|---|
| * | 星号 | 匹配 0 到无穷个字符 | 文件匹配 (Glob) | *.log |
| ? | 问号 | 匹配确切的 1 个字符 | 文件匹配 (Glob) | test?.py |
| 方括号 | 匹配指定范围内的 1 个字符 | 文件匹配 (Glob) | a-z* | |
| {} | 花括号 | 生成任意字符串序列 | 字符串扩展 (Expansion) | {a,b}.txt |
| ~ | 波浪号 | 当前用户的主目录 | 路径扩展 | ~/Desktop |
Standard File Descriptors
(0) stdin:给程序的标准输入,通常来自键盘,也可以重定向来自文件或命令(1) stdout:来自程序的标准输出,通常输出到终端屏幕,也可以重定向到文件或命令(2) stderr:标准错误输出,通常输出到终端屏幕,也可以重定向到文件或命令
File Redirection | 文件重定向
- 重定向标准输入
<从文件重定向输入 ,从infile读入作为输入command < infile
- 重定向标准输出
- 重定向覆盖输出
>重定向(覆盖)标准输出到文件 ,不输出到终端屏幕,输出到outfilecommand > outfile
- 重定向追加输出
>>追加标准输出到文件, 不覆盖,写到文件末尾command >> outfile
- 重定向覆盖输出
- 重定向标准错误输出
2> file重定向标准错误到文件,2代表stderr
- 这一块不同shell各不相同,相见本节下小节
- 管道符与命令
|tee|#命令 1 的标准输出作为命令 2 的标准输入,也就是把command1的stdout接到command2的stdincommand1 | command2tee#复制标准输出:从标准输入读,再写入标准输出和文件(三通)(replicate the standard output)- 举例:
ls -l | tee lslist- 此时
tee会在屏幕上打印ls -l的内容,并将结果写入lslist -a选项为追加append,不覆盖,追加到文件的末尾- 可以通过
tee test1.txt test2.txt来写入两个文件,这样会在屏幕上打印一次(两次应该是版本问题)
- 此时
- 举例:
- 丢弃输出 | To discard output
> /dev/null:将输出送到垃圾箱(黑洞)
标准输出流与标准错误输出流
Csh中标准输出流与标准错误输出流
>& file重定向(覆盖)标准输出和标准错误到文件,&意思可以理解为带上错误>>& file追加标准输出和标准错误到文件,与上一点相同|& command输送标准输出和标准错误到命令,也就是,把stdout和stderr一起通过管道送给下一个命令(command > outfile) >& errfile重定向标准输出和标准错误到不同的文件- 第一步:
command > outfile- 把 stdout(1) 写到
outfile - stderr(2) 仍然默认到屏幕
- 把 stdout(1) 写到
- 第二步:外层的
>& errfile- 它作用的是整个括号里的“命令组”的输出环境
- 把“剩余的输出”(主要是 stderr)也重定向到
errfile
- 第一步:
这些命令在 bash 中也适用
Sh/bash中标准输出流与标准错误输出流
2> file重定向标准错误到文件,2代表stderr>file 2>&1重定向标准输出和标准错误到文件- 拆开来理解(类似于C语言指针的思想)
> file是指重定向标准输出>到文件file2>处理标准错误输出&1,不是文件1,而是stdout- 所以
2>&1的意思是:让 stderr(2)指向 stdout(1)当前指向的地方
- 在 bash/zsh 里有一个更短的语法糖
&> file,等价于这个,即
bash command &> file command > file 2>&1
- 语法糖(Syntactic Sugar): 就是"更甜的语法",让你写起来更简单、更爽,但底层做的事情是一样的。
- 拆开来理解(类似于C语言指针的思想)
>> file 2>&1追加标准输出和标准错误到文件,和上面的区别就是>>2>&1 | command输送标准输出和标准错误到某个命令commandcommand > outfile 2> errfile#重定向标准输出和标准错误到不同的文件>代表正常的输出stdout,写到outfile2>代表错误输出stderr,写到errfile
>& 与 &> 区别
2>&1的含义:将标准错误输出重定向到标准输出。注意:符号>&是一个整体,不可分开,分开后就不是上述含义了。可以比对以下例子进行理解:cat test > file 2>&1> file先执行,stdout 指向 file2>&1后执行,stderr 指向 stdout 的当前位置(即 file)- 结果:stdout 和 stderr 都输出到 file
cat test 2>&1 > file2>&1先执行,此时 stdout 还指向终端,所以 stderr 也指向终端file后执行,stdout 改为指向 file,但 stderr 已经固定指向终端- 结果:stderr 输出到终端,stdout 输出到 file(分离了!)
- 注意与
&>的区别,&>是在 bash/zsh 里一个更短的语法糖——也就是约定俗成的把stdout stderror一起重定向了,将标准错误和标准输出全部送到一个地方
| 语法符号 | 作用 (重定向 STDOUT 和 STDERR) | Csh / Tcsh | Bash | Sh (POSIX) |
|---|---|---|---|---|
| >& file | 将标准输出和错误都写入文件 | 原生支持 (常用) | 支持 (兼容写法) | 不支持 (通常报错或行为不同) |
| &> file | 将标准输出和错误都写入文件 | 不支持 | 原生支持 (推荐语法糖) | 不支持 |
Other Special Command Symbols
;# 命令分隔符,就是cmd1 ; cmd2,cmd1执行完之后执行cmd2,哪怕cmd1执行失败&# 在后台运行命令cmd &&&#只有上一条命令成功完成才运行下一条命令,逻辑与,make && ./a.out编译成功才运行程序||#只有上一条命令没有成功完成才运行下一条命令,比如:cd /some/dir || echo "no such dir"()#圆括号中的分组子命令在子 shell 中执行
(cd /tmp; ls) pwd # 你会发现外面还是原来的目录
Quoting
\#对待下一个字符为纯文本,比如echo \$HOME,输出的是$HOME而不是实际的HOME' '#单引号内不允许变量和命令替代,即$失效,就是按字符串对待" "#双引号内允许变量和命令替代,不在字符串中禁用$和\,和单引号区分一下command或$(command)#用命令的输出代替命令输入
echo "today is $(date)"
Job Control
&#命令在后台执行^Z#当正在运行时,停止该工作,该工作在后台挂起^C#结束当前工作bg#继续在后台挂起的工作bg = back-ground,注意是在后台继续bg + 任务号
fg [%n]#将工作返回到前台(sh被堵住了)fg = fore-groundfg + 任务号,这里的任务号都是jobs中的,不是ps进程号
jobs#列出后台工作(list background jobs)-l列出 pidl = list-r正在后台 run 的工作r = run-s后台中暂停的工作s = stop
ps可以查看进程状态,top进行动态进程监控,而jobs可以查看后台任务
ps/top看的是系统层面的进程
jobs看的是当前这个 shell 启动并管理的任务
[1]+ 27335 Running xeyes &
- 1是任务号
- 27335是进程号
Process Commands
ps
ps [options]#report a photo snapshot of the current processes, show status of process,显示进程状态-ef#e=every processf=full format使用标准语法查看系统上的每个进程,即显示进程详细信息并显示进程父子关系
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:19 ? 00:00:03 /sbin/init splash
root 2 0 0 14:19 ? 00:00:00 kthreadd
root 3 2 0 14:19 ? 00:00:00 pool_workqueue_release
root 4 2 0 14:19 ? 00:00:00 kworker/R-rcu_gp
root 5 2 0 14:19 ? 00:00:00 kworker/R-sync_wq
PPID(Parent Process ID) PID ( Process ID )
-l# long format,显示长信息
top
top#display Linux processes,显示更新的进程信息和系统使用,间隔为 5s
pstree
pstree#显示进程之间的层次关系(show the hierarchical relations between processes)
uptime
uptime#显示该系统的运行时长(tell how long the system has been running)
kill
kill#terminate or signal processes,终止或示意进程kill [-signal] processIDkill -l#列出可用的 kill 信号kill -9 processID强制杀死进程
Environment Variables
先搞懂两类变量:local vs environment
对应sz的 export PATH=$PAT
Shell 变量(local shell variable)
- 只在当前这个 shell里有效
- 你开一个新终端或启动一个子进程,它不一定能看到
例子(bash):
name=Alice echo $name
环境变量(environment variable)
- 会被“导出(export)”,变成环境的一部分
- 启动子进程(比如运行
python、grep、bash)时,子进程会继承这些变量 - 这就是为什么它叫 environment:它是程序运行的“环境”
例子(bash):
export NAME=Alice
一句话记忆
- local:只给自己用
- env:给自己 + 子进程用
- Set global environment variable
setenv NAME value#csh设置环境变量的方式NAME=value; export NAME#sh设置环境变量export NAME=value#bash(export 用于将 shell 变量输出为环境变量)
- Set local shell variable
set name=value#csh设置shell变量name=value#sh/bash设置shell变量
- Unset
unsetenv / unset#csh取消变量unset#sh/bash取消变量
- Showing/Getting Variables
env#csh,bash 也可,只看环境变量set#sh/bash,但 set 会显示更多,因为显示所有变量,env 只是环境变量echo $NAMEecho + $变量名 = 打印变量值
- 当给环境变量赋多个值时,需要用
:间隔不同值 #When assigning multiple values to the variable they must be separated by the colon:character
典型就是 PATH
echo $PATH /usr/local/bin:/usr/bin:/bin:...
- 用
:分割多个目录,shell 查找命令时,会按 PATH 列表顺序去这些目录找
Shell Controls
if 条件
- 最基本的用法,
[ ]单括号为判断,也叫测试,注意[ <-左右两边要有空格-> ],if then elif else fi的格式,记忆一下格式即可
if [ $grade -gt 90 ]; then
echo "good"
elif [ $grade -gt 70 ]; then
echo "pass"
else
echo "no"
fi
options 选项
#这些 -r/-w/-x/... 都是用来判断 某个文件/路径的属性 [ -r filename ] # 可读? [ -d dirname ] # 是目录?
-r#存在且可读 true if it exists and is readable, otherwise return false (0)-w#存在且可写 true if it exists and is writable-x#存在且可执行 true if it exists and is executable-f#存在且是常规文件 true if it exists and is a regular file-d#存在且是目录 true if it exists and is a directory-e#存在 true if the file exists-o#使用者拥有该文件 true if the user owns the file-z#文件大小为 0 / 文件为空 true if the file has zero length (empty)
比较运算
- 只能用于数字:
-gt(greater than)、-lt(less than)、-eq(equal)、-ne(not equal)、-le(less or equal)、-ge(greater or equal) -a(and)、-o(or)、!(not)- 在
[]中不能直接使用&&和||,只能使用-a、-o、!;但在[]外面是可以使用的,如:bash if [ -f $line ] && [ -x $line ]; then code... else code... fi
- 在
[[ ]] 的使用方法
Bash
Zsh
Ksh (Korn Shell)
这三者才兼容这个语法
- 在
[[ ]]中使用>、<等符号不需要转义文字,即>不会被当成重定向,但是注意>比较的是字符串不是数字,数字比较要用-gt-lt,如:bash if [[ $1 > 5 ]]; then echo "$1 的值大于 5" else echo "$1 的值小于 5" fi - 支持
&&和||的括号内使用,如:bash if [[ $a != 3 && $a != 10 ]]; then echo "hello i am linux" fi - 如果不使用
[[ ]]的话则需要这样写:bash if [ $a != 3 -a $a != 10 ]; then echo "hello i am linux" fi - 或者
bash if [ $a != 3 ] && [ $a != 10 ]; then echo "hello i am linux" fi
for 循环语句
基本用法
for 变量名 in 取值列表; do ... done,如下
for Host in $(seq 1 254); do
code...
done
for i in $(find / -name $filename); do
code...
done
for i in {1..5}; do
code...
done
进阶用法:
如果想依次处理传入程序的参数,可以使用 for 与 $* 的结合
$1/$2/$3...是什么?
当你运行:
./demo a b c d
$0= 脚本名(demo)$1= a$2= b$3= c$4= d 记忆:$N = 第 N 个参数,超过10要加${10}
$1:脚本中第一个参数,$2、$3、$4以此类推$*:以一个字符串显示所有向脚本传递的参数,等于"$1 $2 $3 ..."- 示例:假设脚本叫
demo.bash,终端输入./demo a b c d,则参数依次为$1 $2 $3 $4,脚本内容如下:
#!/bin/bash
for i in $*; do
echo "$i"
done
- 这样会依次遍历
$1 $2 $3 $4并处理(这里是打印到屏幕上)

while 循环语句
基本用法:
while 表达式; do 内容 done
- 可以与
break与continue结合,和C语言类似的break是终止循环continue是跳出当前循环
- 例子如下:在死循环中,满足条件终止循环
while true; do
let N++
if [ $N -eq 5 ]; then
break
fi
echo $N
done
- 又如:利用
continue跳过一次循环
N=0
while [ $N -lt 5 ]; do
let N++
if [ $N -eq 3 ]; then
continue
fi
echo $N
done
结果输出:1 2 4
进阶技巧:
利用 while 依次读入文件内容进行处理
while read line; do
echo "$line"
done < temp.txt
line是一个变量名,这里while将依次读取temp.txt每行的内容,并打印- 这个技巧同上面的
for进阶技巧一样,非常好用且重要:可以把要处理的内容保存到文本文件中,再用这个while逐行处理
case 选择语句
基本用法:
case 变量 in 模式1) ... ;; 模式2) ... ;; esac
case 模式名 in
模式1)
命令
;;
模式2)
命令
;;
esac
- 每个模式必须以右括号
)结束(可以是数字或英文) - 每个分支的命令必须以双分号
;;结束,;;就相当于break esac表示 case 结束- 示例:
case $choice in
1)
操作
;;
Start)
操作
;;
esac
终端命令与 History
- 终端:定义指的是与计算机系统相连的一种输入输出设备
tty#print the file name of the terminal connected to standard input
tty = tell terminal type/device
显示当前终端设备的文件名
stty#change and print terminal line settings,改变终端设置stty = set tty- History #GNU history library
history [nn]#显示过去 nn 条命令!!#重复上一条命令!nn#重复history中编号为 nn 的命令!string#重复最近的以 string 开头的命令
Magic Symbol #!
脚本解释器,告诉系统:这个脚本应该用哪个解释器来运行
- sh/bash
#!/bin/sh#!/bin/bash
- csh
#!/bin/csh -f(-foption,without parsing.cshrc)
- 如果没有 Magic Symbol 指示,那么执行脚本命令前必须加上对应的解释器
sh sh_script/csh csh_script/bash bash_script/app_prog app_script
- 但在加上 Magic Symbol 并且
chmod +x script后,我们即可直接运行脚本sh_script/csh_script/bash_script/app_script
Meta-Characters And REP
Meta-Characters as Wild Cards
这些是 shell 里的通配符(wildcards / globbing),主要用于 ls、cp、mv、rm、find(注意:find 本身也有通配/正则的用法)等命令里做文件名/路径匹配。
Meta-Character Rules
?匹配当前位置任意一个字符,即当前位置必须有一个字符(不多不少)ls m?n#m4n, man, m!n
*匹配当前位置任意 0 个或多个字符,即当前位置可以匹配 0 个或多个字符ls *#不是 . 开头的任意文件ls [a-z]*#小写字母开头的任意文件a*匹配:a、ab、abc.txt、a___*.c匹配:main.c、test.c
[abc]#匹配括号内的任意一个字符[a-d]#匹配括号内范围的任意字符[!abc]#匹配任意字符不在封闭集内ls chpt[1-4]#chpt1, chpt2, chpt3, chpt4
{abc,bcd,cde}#匹配任一一组中的任一字符,注意用逗号分隔,花括号展开echo {a,b,c}会展开成:echo a b cls {src,include}/*.h等价于:ls src/*.h include/*.h
~#当前用户的 home 目录~user#指定 user 的 home 目录\#对待下一个字符为纯文本
Regular Expression Syntax
这些是正则表达式(Regular Expression, regex)的基础语法,和通配符很像,但含义不一样,主要给“文本内容匹配”用
REP Rules
cat#字符串 cat.#匹配除换行以外的任意单个字符.at#cat, rat, mat, bat
?#匹配之前单个字符的 0 个或 1 个colou?r匹配:color(u 出现 0 次)、colour(u 出现 1 次)
*#匹配之前的单个字符的 0 或更多实例xy*z匹配:xz(y 0 次)、xyz(1 次)、xyyz(2 次)、xyyyz(3 次)......[A-Z][A-Z]*#A, CD, GPIO,第一位必须是大写字母,后面可以跟 0 或更多个大写字母[A-Z]*# , A, CD,0 或更多个大写字母
[abc]#匹配括号内的任意一个字符gr[ae]y匹配:gray或grey[cC]at#cat 或 Cat
[a-d]#匹配括号内范围的任意一个字符[^abc]#匹配任意字符不在封闭集内[^a-zA-Z]#任意非字母字符
^exp#以该表达式开头的行^cat#cat 在行首
exp$#以该表达式结尾的行cat$#cat 在行尾[0-9]$#以数字结尾的行
\#对待下一个字符为纯文本\*#对待为纯文本星号 通配符(Wildcards/Globbing)主要用于管理文件,而正则表达式(Regular Expressions)主要用于处理文本内容。
特殊字符总结
正则中的特殊字符(需要转义来匹配字面意思)
text . * + ? ^ $ { } [ ] ( ) | \转义序列(需要反斜杠来表示特殊含义)
text \s 空白字符 \d 数字 \w 单词字符 \n 换行 \t 制表符
|#表示或
来个具体例子
bash ❯ ls | grep -E '\(Answer format: "[^"]+"\)' (Answer format: "edlin, edit")匹配过程:
"→ 匹配第一个双引号"[^"]+→ 捕获edlin, edit(所有非引号字符)"→ 匹配第二个双引号"\)→ 匹配右括号)
条件判断部分
第一行包含三个逻辑判断,通常用于决定是否执行后续的 diff 或拷贝操作:
(if -d):- 通常用于检查某个路径是否存在且是一个目录(Directory)。
- 完整写法一般是
if [ -d $DIR_PATH ]。
(if !-w):- 检查文件或目录是否不可写。
!是取反符号,-w代表可写(Writable)。 - 这通常是为了确认目标位置是否有权限限制,或者是否需要提升权限。
- 检查文件或目录是否不可写。
if [ \tty!= "console" ]:- 检查当前的终端类型。
tty命令会返回当前控制台的设备名。- 意思:如果当前用户不是在物理控制台(Console)上直接操作(例如是通过 SSH 远程登录),则条件成立。
命令执行部分
第二行是一个文件对比命令:
diff $SYNOPSYS/admin/setup/$f $LIBX11 >& /dev/null
diff: 比较两个文件的差异。- 源文件:
$SYNOPSYS/admin/setup/$f(通常是 Synopsys EDA 软件的安装配置模板)。 - 目标文件:
$LIBX11(系统路径下的配置文件)。
- 源文件:
>& /dev/null:- 这是一个重定向操作。
- 它将“标准输出”和“标准错误”全部丢弃到
/dev/null(黑洞)。 - 目的:脚本并不想让你看到具体的差异内容,它只是通过
diff的**退出状态码(Exit Status)**来判断两个文件是否一致。- 如果状态码为
0:文件完全一致。 - 如果状态码为
1:文件有差异。
- 如果状态码为
评论区