shell
当我们启动Terminal
时候,在第一行最后会出现ttys0000
1 2 3 Last login: Thu Aug 22 08:30:58 on ttys000 This is .zshrc shell script ... %
tty代表电传打字机(teletypewriter),用于发送消息的机器
常用命令
man
+ cmd : 查看指令的说明
用来访问存储在Linux系统上的手册页面。在想要查找的工具的名称前面输入man命 令,就可以找到那个工具相应的手册条目
1 2 3 4 5 6 man ls --- LS(1) General Commands Manual LS(1) NAME ls – list directory contents
使用q
退出
cd
: 切换目录
这里是一些高级的用法,常规的用法不在此处介绍
直接 使用 cd
或者 cd ~
会回到 HOME 目录下
使用 cd -
会返回 back to 上一个目录
单点符(.
),表示当前目录;
双点符(..
),表示当前目录的父目录
1 2 3 lingxiao@lingxiaodeMacBook-Air Desktop % cd . lingxiao@lingxiaodeMacBook-Air Desktop % cd .. %
pwd
: 查看当前所在的绝对路径
ls
文件和目录列表
基础用法:(略)
可以使用 man ls 查看更过用法
ls -F
-F参数在目录名后加了正斜线(/
); 在可执行文件后面加了*
1 2 3 4 % ls -F OSToDiags* OSToDiags.spec dist/ OSToDiags.py build/ serialTool.py%
ls -F -R
或者 ls -FR
递归查看
ls -l
显示长列表
1 2 3 4 5 6 7 total 8064 -rwxr-xr-x@ 1 lingxiao staff 4115408 Aug 19 15:40 OSToDiags -rw-r--r--@ 1 lingxiao staff 3384 Aug 19 14:44 OSToDiags.py -rw-r--r-- 1 lingxiao staff 718 Aug 19 15:40 OSToDiags.spec drwxr-xr-x 3 lingxiao staff 96 Aug 19 15:40 build drwxr-xr-x 3 lingxiao staff 96 Aug 22 08:54 dist -rw-r--r--@ 1 lingxiao staff 2796 Aug 20 08:58 serialTool.py
touch file
- 创建文件
cp source destination
- 复制文件
mv a b
- 重命名文件或者移动文件
rm -i file
- 删除文件 注意一定要加上 “i”选项
mkdir dir
- 创建目录
mkdir -p parentPath/SubPath
rmdir dir
- 删除目录
默认情况下,rmdir命令只删除空目录。因为我们在New_Dir目录下创建了一个文件my_file, 所以rmdir命令拒绝删除目录。
file + finlName
- 查看文件类型
1 2 3 4 5 6 % file dist dist: directory % file serialTool.py serialTool.py: Python script text executable, Unicode text , UTF-8 text % file OSToDiags.spec OSToDiags.spec: ASCII text
cat + filename
查看文件
ps
查看进程
默认情况下,ps命令只会显示运行在当前控制台下的属于当前用户的进程。
1 2 3 4 % ps PID TTY TIME CMD 6218 ttys000 0:01.49 -zsh%
可以通过 man ps
查看更多用法
kill + PID
结束对应PID
的进程
killall+ProcessName
结束ProcessName的进程(支持通配符模式)
alias
查看所有设置的Shell别名
1 2 3 4 5 6 7 % alias py = python3python = python3run-help = manwhich-command = whenceykb = jekyll %
使用多个命令
Shell中可以将多个命令放在一起;
1 2 3 4 5 6 7 8 9 10 11 12 13 % date ;who ;cal Mon Aug 26 08:31:51 CST 2024 lingxiao console Aug 24 09:19 lingxiao ttys000 Aug 26 08:31 August 2024 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 %
脚本编程基础
在通常的shell脚本中,井号(#)用作注释行。shell并不会处理shell脚本中的注释行。然而, shell脚本文件的第一行是个例外,#后面的惊叹号会告诉shell用哪个shell来运行脚本.
Shell 在使用变量时候的注意点
当 $var 不明确的时候,可以使用**${var}**来使用变量
1 2 3 4 5 6 7 % var=Hello_ % echo "$varWorld " % echo "${var} Wrold" Hello_Wrold ~ %
命令替换
意思是从命令输出中提取信息,并将其赋给变量。
命令替换的两种方式
1 2 3 4 5 6 7 # !/bin/sh testStr=`date` echo "CurrentDate is $testStr" testStr2=$(date) echo "date--- is $testStr2"
输出如下:
CurrentDate is 2024年 8月23日 星期五 13时45分14秒 CST
date— is 2024年 8月23日 星期五 13时45分14秒 CST
运算
shell 的运算通常有两种方式
expr
使用教程:略
使用方括号
1 2 var=$[1 + 5] echo $var ## 6
注意在赋值的时候,需要再方括号前添加$
符号
退出脚本
shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态 码是一个0~255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用。
通过 $?
来获取状态码
当然,我们也可以自己指定退出的状态码 通过 exit code
,请注意 这个code 的最大值不超过255.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # !/bin/sh function test1() { echo $(date) } test1 echo $? ## 0 function test2() { echo $(who) exit 5 ## 这里就已经退出了 当前Shell 进程,并且这个状态码5会在上一次Shell的进程中获取到 } test2# 注意一下的代码不会被执行 echo $?# 这里的也不会被执行 echo $(cal) exit 8 echo "--------" echo $?
通过终端调用上述脚本,我们能够看到
1 2 3 4 5 6 7 % ./004-退出状态码.sh 2024年 8月23日 星期五 15时17分54秒 CST 0 lingxiao console Aug 21 09:15 lingxiao ttys000 Aug 23 15:07% echo $? 5%
Shell 中的 决策树
,(making decision)
最简单的if
1 2 3 4 5 6 7 8 if pwd; then echo "It works ('if' and 'then' locate at some line, must using "\;" to seperation)" fi if pwd then echo "It works" fi
当 if 和 then 在一行的时候,需要再 if 条件语句 添加;
; 如果不在一行就不用
fi
: 是 finish 的缩写,表示 if
语句结束了
值得注意的是,当if 后面的语句是 命令的时候
那么,其执行完成之后的退出状态为0
(即$? == 0
) 时,表示成功,会继续执行 then
部分的语句。
多个条件判断的时候,也可以使用逻辑组合
if - elif - else
if
和 elif
后面 必须跟 then
, 如果在同一行,则需要";"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 test03() { if (( $1 == 1 )); then echo "fist" elif (( $1 == 2 )); then echo "second" else echo "no body using" fi } test03 1 test03 2 test03 3# # #
测试判断条件 – test 命令
if 条件中的语句退出并返回状态码 为 0 的时候,才会执行 其then之后的逻辑;
test命令提供了在if-then语句中测试不同条件的途径。如果test命令中列出的条件成立,test命令就会退出并返回退出状态码0。如果条件不成立,test命令就会退出并返回非零的退出状态码,这使得 if-then语句不会再被执行。
!!! 如果不写test命令的condition部分,它会以非零的退出状态码退出,并执行else语句块
1 2 3 4 5 6 7 8 9 test04() { var="FUll" if test $var; then echo "返回状态码True" else echo "返回状态码False" fi } test04 # 返回状态码True
test命令可以判断三类条件:
测试判断条件 – [ ]
!!! 第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错
1 2 3 4 5 6 7 8 9 test05() { var="Imnt" if [ $var ]; then echo "返回状态码True" else echo "返回状态码False" fi } test05 # 返回状态码True
数值比较
eq : equal
ge: greate or equal
gt: greate than
le: less of equal
lt: less than
ne: not equal
1 2 3 4 5 6 7 8 9 10 test06() { if [ $1 -gt $2 ] then echo "$1 > $2" elif [ $1 -ge $2 ] then echo "$1 >= $2" fi } test06 2 2
字符串比较

!!! 大于号和小于号必须转义,否则shell会把它们当作重定向符号,把字符串值当作文件 名;
文件比较
检查目录
-d
测试会检查指定的目录是否存在于系统中。如果你打算将文件写入目录或是准备切换到某 个目录中,先进行测试接着进行操作。
1 2 3 4 5 6 7 8 9 test07() { if [ -d $1 ]; then echo "this is directory" cd $1 ls -F else echo "not a directory" fi }
If–then的高级用法
用于数学表达式的双括号 (( ... ))
用于高级字符串处理功能的双方括号 [[ ... ]]
双括号
双括号命令允许你在比较过程中使用高级数学表达式;
1 2 3 4 5 6 7 8 9 10 11 test08() { var1=10 if (( $var1 ** 2 > 90 )) then (( var2 = $var1 ** 2 )) # var3=(($var1 ** 2)) echo "The Square of $var1 is $var2" fi } test08 # The Square of 10 is 100
注意:
双括号里的高级的表达式 的运算和赋值只能在其内部进行!!!否则会报错
不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性
双中括号(双方括号)
双方括号命令提供了针对字符串比较的高级特性。双方括号命令的格式: [[ expression ]]
双方括号里的expression使用了test命令中采用的标准字符串比较。但它提供了test命令未提供的另一个特性——模式匹配(pattern matching)。
1 2 3 4 5 6 7 8 9 10 test09() { if [[ $USER == l* ]] ## 这里用到了模式匹配 then echo "HELLO $USER" else echo "sorry, who are u" fi } test09 ## HELLO lingxiao
case 语句
类似于其他语言的switch
语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 test10() { case $1 in rich | brarbara) # 多个条件可以使用 `|` 分割,相当于 逻辑 或 echo "Welcome. $1" echo "Pls enjoy yourself" ;; ##相当于 break testing) echo "Sepcial Testing" ;; jessica) echo "I'm $1" ;; *) ## 星号(`*`)会捕获所有与已 知模式不匹配的值 echo "Sorry,you are not allowed" ;; esac }
循环
for - 循环
1 2 3 for var in list do commands done
可以用for命令来自动遍历目录中的文件。进行此操作时,必须在文件名或路径名中 使用通配符(*)
。它会强制shell使用文件扩展匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 test02() { ## 如果想要遍历某一个文件夹下的所有内容,需要使用通配符 path="/Users/lingxiao/.personal/study_and_code/shell-tutorial/*" # path="/Users/lingxiao/.personal/study_and_code/shell-tutorial" for file in $path do # 这里需要注意下,如果file中包含 空格,需要把`$file` 通过 双引号 `"$file"` 包起来,否则会报错;(bash shell会将额外的单词当作参数,进而造成错误) if [ -d "$file" ]; then echo "$file is a directory" elif [ -f "$file" ]; then echo "$file is a raw file" fi done }
for 循环更多练习,列出系统中所有在 $PATH
环境变量中指定目录下的可执行文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 test04() { IFS=: for folder in $PATH do echo "$folder:" for file in $folder/* do if [ -x $file ] then echo " $file" fi done done }
方法中第一行 IFS=:
解释
作用 : 将Shell的内部字段分隔符(IFS
)设置为冒号(:
)。
原因 : 在Shell中,IFS
用于指定在变量展开和命令替换时,如何分隔字符串。默认情况下,IFS
包含空格、制表符和换行符。将其设置为冒号后,可以方便地将 $PATH
环境变量按照冒号分隔,遍历其中的每个目录。
C-Style For循环
需要使用高级运算符 双括号, 同时 双括号 内部的变量 也不需要使用 $
1 2 3 4 5 6 test03() { for (( var = 1; var < 10; var++)) ## 注意这里操作 var 不需要添加 `$`符号 do echo "index == $var" done }
while循环
1 2 3 4 5 6 7 8 test01() { declare var=0 while [ $var -lt 10 ]; do # ((var++)) ##高级运算符里不需要带`$` var=$[ $var + 1 ] echo "$var" done }
循环输出的重定向
如下示例,在循环结束表示done
后,添加重定向的命令
1 2 3 4 5 6 7 8 9 10 11 12 test02() { ## 如果想要遍历某一个文件夹下的所有内容,需要使用通配符 path="/Users/lingxiao/.personal/study_and_code/shell-tutorial/*" for file in $path do if [ -d $file ]; then echo "$file is a directory" elif [ -f $file ]; then echo "$file is a raw file" fi done >> ./Redirections/log.y0823 ## 重定向 }
处理用户输入
如果只想要获取脚本名称可以使用basename
命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # !/bin/sh echo "\$0 == $0" #此处获取的是脚本的路径 scriptName=$(basename $0) #此处获取 脚本的文件名称 echo $scriptName echo "1 == $1" echo "2 == $2" echo "3 == $3" echo "4 == $4" echo "5 == $5" echo "6 == $6" echo "7 == $7" echo "8 == $8" echo "9 == $9" echo "10 == ${10}" echo "11== ${11}" echo "12 == ${12}" echo "\$# total args count is == $#" echo "last args var is == ${$#}" ## 出错了last args var is == 69701,方括号里不能出现美元符 echo "last args var is == ${!#}" ## 把美元符改成!就OK last args var is == 12 echo "获取所有参数 \$*-- $*" # 获取所有参数 $*-- 1 2 3 4 5 6 7 8 9 10 11 12 echo "获取所有参数 \$@-- $@" # 获取所有参数 $@-- 1 2 3 4 5 6 7 8 9 10 11 12
$*
和$@
的区别
$*
和$@
在没有添加引号的时候,两者一样
当添加了引号,"$*"
会把所有的参数当成一个字符串,而"$@"
会把参数当成一个数组
接着上述代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # # echo "---------\"\$*\"-------" count=1# for param in "$*" do echo "\$* Parameter #$count = $param" count=$[ $count + 1 ] done echo "---------\"\$@\"-------" count=1# for param in "$@ " do echo "\$@ Parameter #$count = $param" count=$[ $count + 1 ] done
上述代码输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ---------"$*"-------$ * Parameter ---------"$@"-------$ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter $ @ Parameter
脚本中read
命令
read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令 会将数据放进一个变量。
1 2 3 4 5 6 7 # !/bin/sh test01() { echo "Enter Your Name : " read name ##把输入的字符赋值给变量`name` echo "Hello $name, Welcome To Our World" } test01
上述代码输出结果:
Enter Your Name :
lingxiao
Hello lingxiao, Welcome To Our World
Shell函数
基础概念
函数是一个脚本代码块,即一个代码块,这在其他语言中也是这样解释。
函数定义的两种方式
1 2 3 4 5 6 7 8 9 function func_1 { # 名称后没有小括号 echo "hello world 001" } func_1 ## hello world 001 func_2() { ## 名称后面会有 小括号 echo "hello world 002" } func_2 ## hello world 002
跟其他大多数一样,函数必须先声明才能使用。
案例:
输出 1到10 的数字
1 2 3 4 5 6 7 8 9 loop_test() { count=0 while [ $count -lt 10 ]; do count=$[ $count + 1 ] echo "count == $count" done } loop_test
返回值
Shell
会把函数当成一个小脚本,运行结束之后,会返回一个状态码
1. 默认退出状态码
默认情况下,函数退出的状态码就是函数内部最后一条指令的退出状态码,可以通过$?
来获取。
我们接上述loop_test
的函数来测试,我们能够看到返回的是0
;标识函数运行成功
1 2 3 4 loop_test echo $? ## 0
2. 使用return
命令
shell使用return命令来退出函数并返回特定的退出状态码
1 2 3 4 5 6 7 8 double_value() { read -p "Enter a value: " value ## -p 选项允许你在读取用户输入之前显示一个提示信息(prompt)。在这里,提示信息是 "Enter a value: "。 echo "Doubling The Value" return $[ $value * 2 ] ## 返回特定的 退出状态码 } double_value echo "The New Value is $?"
上述代码的输出如下:
1 2 3 Enter a value: 100 Doubling The Value The New Value is 200
这里需要注意两点
调用函数结束之后就需要执行获取状态码的命令$?
;
如果在用$?
变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住,$? 变量会返回执行的最后一条命令的退出状态码。
状态码的取值范围是0~255
所以如果要返回较大整数值或者是字符串的话,这种返回则不能够满足。
3.使用函数输出
我们函数的输出保存到一个变量中
1 2 3 4 5 6 7 function double_value2 { read -p "Enter a value: " value # echo "Doubling The Value" echo $[ $value * 2 ] } result=$(double_value2) #注意这的函数调用,会把函数中所有 echo 的内容 赋值给 result echo "The New Value2 is $result"
输出结果如下:
1 2 Enter a value: 125 The New Value2 is 250
新函数会用echo语句来显示计算的结果。该脚本会获取double_value2
函数的输出,而不是查看退出状 态码。