shell脚本详细学习

shell

passwd 文件:多行多列的表格
列的分隔符是冒号

root:x:0:0:root:/root:/bin/bash

第一列是用户名:root

第二列是密码,但是通过加密放到了shadow中

第三列是用户id

第四列是组id

第五列是用户描述

第六列是home目录地址

第七列是用户登录之后执行的第一条命令

确定用户当前用什么shell

1.echo $SHELL

2.看 passwd 文件用户的启动命令

shell内建命令

比如 cd、alias、umask 在which xxx中找不到,就是内建命令,这不是一个进程只是调用了shell进程的函数

shell脚本

shell脚本相当于把一行行命令集中一次性处理,在执行命令的过程中会fork一个子进程并调用exec执行./test.sh这个程序,exec系统调用把子进程的代码段替换成./test.sh程序的代码段,并从他的_start开始执行 但是./test.sh只是个文本文件,根本没用代码段和——start函数,怎么解决呢?实际上exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器的_start开始执行,而这个文本文件被当作命令行参数传给解释器。

eg:

#!bin/bash  # 指定用的解释器
echo Helloworld
cd ..
ls

执行脚本的几种方式:

可以用 ./test.sh执行;

也可以用 source 脚本地址,**这种方式执行脚本不会产生一个子进程,而是将脚本的命令都加载进来执行**

还可以用 . 脚本地址,跟 source 一样

shell 基本语法

变量

变量的定义

VARBNAME=value

变量的使用,用 $ 符号跟上变量名表示对某个变量进行取值,变量名可以加上花括号来表示变量名的范围

echo $VARBNAME

echo ${VARBNAME}
VARBNAME=value

注意:等号两边都不能有空格!!

变量的分类

* shell 内变量 (这个变量只存于shell内)
    > 全局变量 (shell中不使用任何修饰符修饰的变量都是全局变量)
    > 局部变量 (用 local 来修饰)
shell内变量只能在当前shell进程中使用,跨进程不允许使用多个子进程

* 环境变量
操作系统自带,每个进程都会有 
当启动一个子进程时,环境变量是从父进程拷贝到子进程

全局变量以及局部变量 eg:

#!/bin/bash
global="hello"

function test()
{
        global2="world"
        local LocalValue="cast"
        echo $LocalValue
}

test
echo $global $global2
echo $LocalValue #这里local是局部变量在函数结束的时候就已经全部释放了,自然看不到输出结果

环境变量 eg:

export varname=value

删除变量

用unset命令可以删除已定义的环境变量或本地变量 不管是环境变量还是普通的shell变量,都可以使用unset删除

unset VARNAME

文件名代换

用于匹配的字符统称为通配符

* 匹配0个或多个任意字符
? 匹配一个任意字符
[若干字符] 匹配方括号中的任意一个字符的一次出现 

代换文件的前提:文件必须存在,如果不存在是代换不了的

命令代换

shell先执行该命令,然后将输出结果立刻代换到当前命令行中
eg:

#!/bin/bash
DataTime=`date`  # 由"`"这个符号也是一条命令,相当于先把这个命令保存再输出,与 $(date)的功能一样
echo "DateTime is " $DataTime

算数代换

使用$(()),用于算术计算,(())中的Shell变量取值将转换成整数,跟$[]等价
注意:$(())中只能用加减乘除和()运算,并且只能做整数运算
eg:

#!/bin/bash
VAR=45
echo $((VAR+3))

使用 $[base#n] 做进制计算,其中base表示进制,n按照base进制解释,后面再有运算数,按十进制解释
eg:

#!/bin/bash
echo $[8#10+11]

shell引号

Shell脚本中单引号和双引号都是字符串的界定符,而不是字符的界定符。单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号
单引号eg:

 echo 'aaa
> bbbb'
aaa
bbbb

双引号eg:
被双引号括住的内容,将被视为单一字串,它防止通配符扩展,但允许变量扩展
eg:

#!/bin/bash
var="a b"
echo $var

输出的结果就是 a b 中间有空格,但是我想 var 是一个整体我并不想把他拆开变量,于是就可以用 echo "$var"

条件测试

条件测试是用来表示真假,直接使用某条命令的返回状态(main函数返回值)
若 main()函数返回0,即为真,若main函数返回非0,即为假
如何获取返回状态?

通过 $? 来获取上一条命令的返回状态
使用命令 test 或 [ 可以测试一个条件是否成立

        ! EXPRESSION(取反)
              EXPRESSION is false

       EXPRESSION1 -a EXPRESSION2 (与)
              both EXPRESSION1 and EXPRESSION2 are true

       EXPRESSION1 -o EXPRESSION2 (或)
              either EXPRESSION1 or EXPRESSION2 is true

       -n STRING (判断字符串不是空串)
              the length of STRING is nonzero

       STRING equivalent to -n STRING

       -z STRING (判断字符串长度为0)
              the length of STRING is zero

       STRING1 = STRING2 (判断字符串相等)
              the strings are equal

       STRING1 != STRING2(判断字符串不相等)
              the strings are not equal

       INTEGER1 -eq INTEGER2 (判断整数相等)
              INTEGER1 is equal to INTEGER2

分支结构

if结构

结构:if 命令|条件测试
    then xxxxx
    elif 命令2|条件测试
    else 
        xxxx
    fi

如果then跟if写在同一行,加分号,else后不需要跟then
eg:

#!/bin/bash
if [ -f /bin/bash ]
then echo "Bash is installed."
else
    echo "Bash is not installed."
fi

case结构

case命令可类比C语言的 switch/case语句,esac表示case语句块的结束,Shell的 case 可以匹配字符串和 Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束

结构:
case 表达式 in 
val1|pattern1)
xxxxx
;;
val2|pattern2)
;;
esac 

eg:

#!/bin/bash
echo "Is it morning or evening? (morning/evening)"
read time_of_day
case "$time_of_day" in
  morning)
    echo "Good morning!"
    ;;
  evening)
    echo "Good evening!"
    ;;
  *)
    echo "Invalid input. Please enter 'morning' or 'evening'."
    ;;
esac

循环

for循环

结构:
for varname in 列表
do 
done

for varname in 列表; do
done

eg:

#!/bin/bash
for F in Banana apple pear;do
echo "I like $F"
done
#!/bin/bash

sum=0
for F in {1..100}; do
    sum=$((sum + F))
done
echo "The sum is: $sum"

while 循环

结构:
与C类似
while 命令|条件测试
do
  xxxx
done

eg:

#!/bin/bash

sum=0
cnt=1
while [ $cnt -le 100 ] ; do
    sum=$((sum + cnt))
    cnt=$((cnt + 1))
done
echo "The sum of numbers from 1 to 100 is: $sum" 
#!/bin/bash

echo "Enter Password:"
read password
while [ "$password" != "manager" ] ; do
    echo "Please try again"
    read password
done
echo "Access granted"

位置参数和特殊变量

$0   相当于C语言 main 函数的 argv[0]
$1、$2...   这些相当于位置参数,类似C语言中的 argv[1]、argv[2]...
$@    表示参数列表"$1""$2"...可以用在for循环中in后面
$? 上一条命令的返回状态
$$ 当前进程号

eg:

#!/bin/bash
sum=0
for i in $@;do
	sum=$[$sum+$i]
done
echo "The sum is $sum"

注意: 位置参数默认就支持10个,$@还是支持很多个

管道 (命令与命令的交互)

可以通过 | 把一个命令的输出传递给另一个命令做输入,实际上是将前面进程的标准输出重定向到后边进程的标准输入,标准错误输出是不会重定向的
eg:

#include <stdio.h>
#include <ctype.h>
int main()
{
        int ch = getchar();
        while(ch != EOF)
        {
                putchar(toupper(ch));
                ch = getchar();
        }
        return 0;
}
echo abc | ./main

衍生:

* more命令 ,将标准输入的内容(超级多)进行缓慢向下查看,只支持向下走,不支持往回走
* less 命令,比 more 更完善,支持往回滚,支持类似 vim 的操作
* tee 命令 ,把结果输出到标准输出,另一个副本输出到相应文件

重定向 (文件与命令的交互)

* cmd > file  把标准输出重定向到新文件中
* cmd >> file 追加
* cmd > file 2>&1 标准出错也要重定向到1所指向的file里
* cmd >> file 2>&1    文件描述符2重定向到文件描述符1中
* cmd < file1 > file2 输入输出分别定向到两边文件中
* cmd < &fd     把文件描述符fd作为标准输入
* cmd > $fd     把文件描述符fd作为标准输出
* cmd < &-        关闭标准输入

函数

在Shell中函数定义中没有返回值也没有参数列表

结构:
func 函数名()
{
    return 1
}
注意:func可以省,小括号也可以省,但是这两个必须保留一个 小括号里面也不需要填列表参数,return 只能返回参数,不能返回其他类型

eg:

#!/bin/bash
foo()
{
        echo "This is a lion."
        return 1
}
foo 
echo $?

testfunc()
{
        echo "aaaaaaaa"
        return 0
}
var=`testfunc` # 将反引号内的命令执行,并用执行结果替换掉整个反引号部分
#var=$(testfunc)
echo ${var}

shell调试方法

* -n    读一遍脚本中的命令但不执行,用于检查脚本中的语法错位
* -v    一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
* -x    提供跟踪执行信息,将执行每一条命令和结果依次打印出来

可以跟在 命令行后 bash -x xx.sh,也可以在代码前显示 #!/bin/bash -x

正则表达式

结构:
1.以S开头的字符串    ^S匹配
2.以数字结尾的字符串  
[0123456789] [0-9] \d   [0-9]$

3.匹配空字符串(没有任何字符)  
^$匹配

4.字符串只包含三个数字       
^\d\d\d$  控制次数 我们有更好的办法写 ^\d{n}$ 表示前面的单元重复n次

5.字符串只有 3-5 个  
{m,n}   m表示前面单元最小重复次数,n表示最大重复 次数
字母表示法:[a-zA-Z] PS:**如果中括号中有多个区间,区间之间不要留空格或其他分隔符**
所以 ^[a-zA-Z]{3,5}$

6.匹配不是a-z的任意字符
[^a-z] 若 ```^```这个符号出现在中括号内第一个表示**区间取反**

7.字符串有0到1个数字或者字母或者下划线
{0,1} 表示重复0-1次, ? 也可以表示重复0-1次
^[0-9A-Z_]?$

8.字符串有1个或多个空白符号 
\s 表示有空格出来的字符 包括 \t\n\r...
{1,n}表示重复 1-n次,与 ```+```这个符号一样
^\s+$

9.字符串有0个或若干个任意字符
. 代表任意字符,除 \n
{} 花括号中两个参数置空表示重复次数任意 0-n 
* 表示前面的单元重复 n 次 (0-n)
  ^.*$ 


总结: 
? 0-1
+ 1-n
* 0-n

10.匹配0或任意多组ABC,比如ABC,ABCABCABC
在描述多组ABC时,可以用 ()来表示多个单元重新组成一个单元
^(ABC)*$

11.字符串内要么是ABC,要么是123
需要做选择的时候,需要写 ```|``` 选择两边的一个
^(ABC)$|^(123)$


12.字符串只有一个点号
因为 ```.```具有特殊含义,表示任意字符,所以我们需要对其进行转义
^\.$

13.匹配十进制3位整数
十进制三位数有:100-999
^[1-9][0-9]{2}$

14.匹配十进制 0-999 数字
这种情况需要 一位数、二位数、三位数分类
一位数:^[1-9][0-9]$
二位数:^[1-9][0-9]$
三位数:^[0-9][0-9]{2}$
^[0-9]|[1-9][0-9]{1,2}$

shell中的其他命令

sort 命令

命令从标准输入中读取数据然后按照字符串内容进行排序

-f      忽略字符大小写
-n      比较数值大小
-t      指定分隔符,默认是空格或者tab
-k      指定分割后进行比较字段
-u      重复行只显示一次
-r      反向排序
-R      打乱顺序 
cat test.txt | sort 与 sort < test.txt一样

uniq

去除重复的行,前提是重复的行连续
-c       显示每行重复的次数
-d       仅显示重复的行
-u       仅显示不曾重复的行

eg:

sort < test.txt | uniq -c
      1 xiao 89 man 16
      1 xiaow 10 man 11
      1 xiaowen 78 man 90
      2 xio 30 man 50
sort < test.txt | uniq -d
xio 30 man 50
sort < test.txt | uniq -u
xiao 89 man 16
xiaow 10 man 11
xiaowen 78 man 90

wc命令 (word count)

* -l 统计行数
* -c 统计字节数
* -w 统计单词数
wc -l < test.txt
5
cat test.txt
xiaowen 78 man 90
xiao 89 man 16
xiaow 10 man 11
xio 30 man 50
xio 30 man 50
wc -c < test.txt
77
wc -w < test.txt
20

grep (Global Regular Expression Print)

grep 命令是一种强大的文本搜索工具,能使用正则表达式搜索文本,并把匹配的行打印出来,使用权限是所有用户

格式以及主要参数

Usage: grep [OPTION]... PATTERNS [FILE]...
Search for PATTERNS in each FILE.
Example: grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.

Pattern selection and interpretation:
  -E, --extended-regexp     PATTERNS are extended regular expressions
  -F, --fixed-strings       PATTERNS are strings
  -G, --basic-regexp        PATTERNS are basic regular expressions
  -P, --perl-regexp         PATTERNS are Perl regular expressions
  -e, --regexp=PATTERNS     use PATTERNS for matching
  -f, --file=FILE           take PATTERNS from FILE
  -i, --ignore-case         ignore case distinctions in patterns and data
      --no-ignore-case      do not ignore case distinctions (default)
  -w, --word-regexp         match only whole words
  -x, --line-regexp         match only whole lines
  -z, --null-data           a data line ends in 0 byte, not newline

Miscellaneous:
  -s, --no-messages         suppress error messages
  -v, --invert-match        select non-matching lines
  -V, --version             display version information and exit
      --help                display this help text and exit

Output control:
  -m, --max-count=NUM       stop after NUM selected lines
  -b, --byte-offset         print the byte offset with output lines
  -n, --line-number         print line number with output lines
      --line-buffered       flush output on every line
  -H, --with-filename       print file name with output lines
  -h, --no-filename         suppress the file name prefix on output
      --label=LABEL         use LABEL as the standard input file name prefix
  -o, --only-matching       show only nonempty parts of lines that match
  -q, --quiet, --silent     suppress all normal output
      --binary-files=TYPE   assume that binary files are TYPE;
                            TYPE is 'binary', 'text', or 'without-match'
  -a, --text                equivalent to --binary-files=text
  -I                        equivalent to --binary-files=without-match
  -d, --directories=ACTION  how to handle directories;
                            ACTION is 'read', 'recurse', or 'skip'
  -D, --devices=ACTION      how to handle devices, FIFOs and sockets;
                            ACTION is 'read' or 'skip'
  -r, --recursive           like --directories=recurse
  -R, --dereference-recursive  likewise, but follow all symlinks
      --include=GLOB        search only files that match GLOB (a file pattern)
      --exclude=GLOB        skip files that match GLOB
      --exclude-from=FILE   skip files that match any file pattern from FILE
      --exclude-dir=GLOB    skip directories that match GLOB
  -L, --files-without-match  print only names of FILEs with no selected lines
  -l, --files-with-matches  print only names of FILEs with selected lines
  -c, --count               print only a count of selected lines per FILE
  -T, --initial-tab         make tabs line up (if needed)
  -Z, --null                print 0 byte after FILE name

Context control:
  -B, --before-context=NUM  print NUM lines of leading context
  -A, --after-context=NUM   print NUM lines of trailing context
  -C, --context=NUM         print NUM lines of output context
  -NUM                      same as --context=NUM
      --group-separator=SEP  print SEP on line between matches with context
      --no-group-separator  do not print separator for matches with context
      --color[=WHEN],
      --colour[=WHEN]       use markers to highlight the matching strings;
                            WHEN is 'always', 'never', or 'auto'
  -U, --binary              do not strip CR characters at EOL (MSDOS/Windows)

When FILE is '-', read standard input.  With no FILE, read '.' if
recursive, '-' otherwise.  With fewer than two FILEs, assume -h.
Exit status is 0 if any line is selected, 1 otherwise;
if any error occurs and -q is not given, the exit status is 2.

pattern 正则表达式主要参数:(pattern - 表示要查找的字符串或正则表达式。)

* \ 忽略正则表达式中特殊字符的原有含义
* ^ 匹配正则表达式的开始行
* $ 匹配正则表达式的结束行
* \< 从匹配正则表达式的行开始
* \> 到匹配正则表达式的行结束
* [ ] 单个字符,eg:[A] 即A符合要求
* [ - ] 表示字符范围,eg:[A-Z],即A~Z都符合要求

eg:

在文件 file.txt 中查找字符串 "hello",并打印匹配的行:
grep hello file.txt
在文件夹 dir 中递归查找所有文件中匹配正则表达式 "pattern" 的行,并打印匹配行所在的文件名和行号:
grep -r pattern ~/shell/create

find 命令

find 用于在指定目录下查找文件和目录,可以使用不同的选项来过滤和限制查找的结果

结构以及常见的匹配条件:

find [路径] [匹配条件] [动作]

* -name 按照文件名查找文件
* -perm   按照文件权限来查找文件
* -prune  使用这一选项可以使 find 命令不在当前指定的目录中查找,如果同时使用 -depth选项,那么-prune 将被 find 命令忽略
* -user  按照文件属主来找文件
* -group 按照文件所属的组来找文件
* -mtime days:按修改时间查找,支持使用 + 或 - 表示在指定天数前或后,days 是一个整数表示天数。
* -type  查找某一类型的文件 eg:b(块设备文件)、d(目录)、c(字符设备文件)、p(管道文件)、l(符号链接文件)、f(普通文件)、s(socket文件)
* -exec 可以作为一个指令执行

eg:

查找当前目录下名为 file.txt 的文   件:
find . -name file.txt
查找当前目录下的txt文件并且打包成为 gzip
find . -name "*.txt" -exec gzip {} \;

xargs

将标准输入的参数整齐拼凑在一行里面 一般是和管道一起使用

sed(Stream Editor)

sed 在 shell 脚本 和 Makefile 中作为过滤器使用非常普遍,也就是把前一个程序的输出引入 sed 的输出,经过一系列编辑命令转换位另一种格式输出 还有一种说法就是它利用脚本来处理文本文件

结构:

sed option '/pattern/action' file1 file2 ...
解释: sed 选项参数 '/pattern/action'(其中pattern是正则表达式,action是编辑操作) 待处理文件
sed option -f scriptfile file1 file2
解释: sed 选项参数 -f '脚本文件' 待处理文件

* -e  用来指定一段脚本(编辑命令)
* a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
* c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
* d :删除,因为是删除啊,所以 d 后面通常不接任何东东;
* i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
* p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~

eg:

sed '' test10.txt
ashfjklsadfsndhlkj
ashfjiksdahfkl
asdjfio
lllll999999999
oiopi90
123
-------------------------------
sed -e '4a helloworld' test10.txt
# sed -e '4a\helloworld' test10.txt
ashfjklsadfsndhlkj
ashfjiksdahfkl
asdjfio
lllll999999999
helloworld
oiopi90
123

awk

sed 以行单位处理文件,awk 比 sed 强的地方在于不仅能以行为单位还能以列为单位处理文件。**awk缺省的行分隔符是换行,缺省的列分隔符是 连续的空格 和 Tab**,当然,行分隔符和列分隔符都可以自定义

结构:

awk option 'script' file1 file2
awk option -f scriptfile file1 file2
**和 sed 类似,pattern 是正则表达式,actions是匹配成功后一系列操作**
命令行参数传递方式:
* /pattern/{actions}
* condition/{actions}

关于 awk 脚本,我们需要注意两个关键词 BEGIN 和 END。

**BEGIN{ 这里面放的是执行前的语句 }**
** END {这里面放的是处理完所有的行后要执行的语句 }**

eg:

 awk '{print $1}' test10.txt
ProduceA
ProduceB
ProduceC

以C语言的方式理解 awk 命令

$2 < 75
{
    printf("%s\n%s\t","reorder");
}
$2 > 75 
{
    printf("%s\n",$0);
}
posted @ 2026-09-07 11:39  Alaso_shuang  阅读(17)  评论(0)    收藏  举报
2 3
4