一言
永远相信美好的事情即将发生——小米
Linux学习笔记:shell脚本
本文最后更新于 371 天前,其中的信息可能已经有所发展或是发生改变。

shell概论

shell

    shell的中文翻译是外壳,是一种计算机操作系统中的用户界面,提供了与操作系统内核进行交互的途径。它是用户与操作系统之间的中间层,允许用户执行命令、运行程序和管理文件系统等操作。
    总的来说shell是一个命令解释器,比如Linux中的ls、cd、pwd等等命令都属于shell命令。通过接受用户输入的Shell命令来与操作系统进行交互
    常见的shell有:
Linux、unix、macOS:

  • Bourne Shell(/usr/bin/sh或/bin/sh)
  • Bourne Again Shell(/bin/bash)
  • C Shell(/usr/bin/csh)
  • Korn Shell(/usr/bin/ksh)
  • Z Shell(/usr/bin/zsh)

Windows:

  • Command Prompt
  • PowerShell

shell脚本

Shell脚本是就是一种由Shell命令组成的文本文件,我们可以编写自己的shell脚本来实现一定的功能,以便于后来复用。Linxu中一般bash用的比较多。
脚本文件开头需要指定脚本使用的解释器:
#! /bin/bash
#! 被称为"shebang"或"hashbang",它告诉操作系统该脚本应该由哪个解释器来执行。如果写的是python脚本就在文件开头写入#! /usr/bin/env python

shell脚本的运行方式、

例如:新建一个test.sh文件写入:echo "Hello World"

  1. 用解释器执行:

    [root@VM-8-17-centos Linux-Study] bash test.sh 
    Hello Longlong!
  2. 作为可执行文件执行:如果直接执行:

    [root@VM-8-17-centos Linux-Study] ./test.sh
    bash: ./test.sh: Permission denied

    可以看到是拒绝执行的,我们需要使他有可执行权限:
    使用chmod +x命令

    [root@VM-8-17-centos Linux-Study] chmod +x test.sh
    [root@VM-8-17-centos Linux-Study] ./test.sh
    Hello Longlong!

shell注释

  1. 单行注释:
    #这是单行注释
  2. 多行注释:
    :<<anyword
    第一行注释
    第二行注释
    第三行注释
    anyword

    anyword 可以替换任意值,甚至中文都可以, 只要上下对应上就行

shell变量

定义:

variable_name=value注意,在等号两边不能有空格。有空格的话系统会认为空格之后该命令就结束了,然后会将value当成一个命令
例如:

x= hello #报错:hello: command not found

bash中默认都是字符串类型的,也可以这么定义:variable_name='value'或者variable_name="value"
以上三种并无大区别
但是当你想要变量的值中含有空格的时候,你就需要加引号了:
x="hello word"

引用变量:

$来引用变量的值

x="hello word"
echo $x  # 输出hello word
echo ${x}  # 输出hello word
echo ${x}6666  # hello word6666

虽然说不带花括号也行,但一般还是带上,可以识别你真正的变量,比如第三个例子

只读变量

可以使用readonly或者declare -r关键字将变量设置为只读,这样一旦赋值后就无法再修改。
例:


x=hello
readonly x
declare -r x

删除变量

使用关键字:unset可以删除变量
其实所谓删除变量,就是将变量置为空,因为bash中严格来说变量是不用定义的.就比如直接在命令行中输出:echo $变量名,也不会报错, 只会输出空行。所以这个unset关键词等价于将x置为空x=''

x=111
echo ${x} #输出111
unset x
echo ${x}#输出空一行
x=111
x=    #或者x=""、x=''都可以
echo $x #输出空一行

环境变量(全局变量)

环境变量是一种特殊类型的变量,它们由操作系统或Shell设置,如果被设为环境变量,那么它将在当前Shell会话以及所有由该Shell启动的子进程中可用。可以使用export命令或者declare -x 变量名将普通变量提升为环境变量。

temp='11'
export xx #或者 declare -x temp

相反的,有全局变量就有局部变量,脚本内以默认形式自定义的变量默认就是局部变量.
同时也可以将环境变量变为局部变量

export temp=11
declare +x name

字符串变量

  1. 单引号和双引号的区别:
    • 单引号中的内容会原样输出,不会执行、不会取变量;
    • 双引号中的内容可以执行、可以取变量;
      例:

      temp='这里是变量'
      echo 'hello,$temp,\"11\"' #输出hello,$temp,\"11\"
      echo "hello,$temp,\"11\"" #输出hello,这里是变量,"11"
  2. 获取字符串长度以及字串
    • 获取长度:${#变量名}
    • 提取字串:${变量名:0:5}

特殊变量:

Bash还提供了一些特殊的预定义变量

$0: 代表当前脚本的名称(或命令)。在脚本中,它用于获取当前脚本的名称。

$1, $2, $3, ...: 代表脚本的位置参数,即脚本执行时传递的参数。$1表示第一个参数,$2表示第二个参数,以此类推。

$*: 由所有的位置参数构成的用空格隔开的字符串。它表示所有传递给脚本的参数。

$@: 每个位置参数分别用双引号括起来的字符串。它表示所有传递给脚本的参数,每个参数都以双引号括起来。

$#: 代表位置参数的个数,即传递给脚本的参数个数。

$$: 代表当前脚本的进程ID(PID)。在脚本中,它用于获取当前脚本的进程ID。

$?: 代表上一条命令的退出状态(exit code)。0表示命令成功执行,其他值表示命令执行出错。

$!: 代表后台运行的最后一个进程的进程ID。在脚本中,它用于获取后台进程的PID。

$IFS: 代表输入字段分隔符(Internal Field Separator),用于指定字段(单词)之间的分隔符,默认为包含空格、制表符和换行符的字符串。
$(command):返回command这条命令的stdout(可嵌套)
\command\ :返回command这条命令的stdout(不可嵌套)
例:

#! /bin/bash

echo "文件名:"$0
echo "第一个参数:"$1
echo "第二个参数:"$2
echo "第三个参数:"$3
echo "第四个参数:"$4
echo "所有参数1:" $*
echo "所有参数2:" $@
echo "参数个数:" $#
echo "当前脚本的pid:" $$
echo "上一条命令的退出状态:"$?
echo "后台运行的最后一个进程的PID:"$!
echo "输入的字段分隔符":$IFS

然后在终端执行:

[root@VM-8-17-centos Linux-Study] ./test.sh one two three four

输出:
文件名:./test.sh
第一个参数:one
第二个参数:two
第三个参数:three
第四个参数:four
所有参数1: one two three four
所有参数2: one two three four
参数个数: 4
当前脚本的pid: 2672280
上一条命令的退出状态:0
后台运行的最后一个进程的PID:
输入的字段分隔符:

$(command):返回command这条命令的stdout(可嵌套)
\command\ :返回command这条命令的stdout(不可嵌套)
关于这两个:
就是例如: ls这个命令,会有stdout:就是列出当前目录下的所而 \ls\或者$(command)就会将这个stdout给获取了,之后你可以将这个赋给其他变量值或者写道一个文件里,再或者可以直接输出,
例:

temp=$(ls)   #或者是`ls`
echo $temp

输出
test.sh
而这个能不能嵌套就是指的是$(command)
比如:

listing=$(ls -l $(cat filenames.txt))

假设有一个名为 filenames.txt 的文本文件,其中包含一系列文件名,每个文件名占据一行。命令的目标是读取 filenames.txt 文件中的文件名,并将每个文件名作为参数传递给 ls -l 命令,获取这些文件的详细列表信息,并将结果存储在 listing 变量中。
但是如果用``的话就得用转义符,而不能直接嵌套

listing=`ls -l \`cat filenames.txt\``

关于更多这两个命令的区别参考What is the difference between $(command) and command in shell programming?

shell数组

定义

array=(变量1 变量2 变量3)

变量之间用空格隔开
也可以直接赋值

array[0]=变量1
array[1]=变量2
array[2]=变量3

读取整个数组

${array[@]}  # 第一种写法
${array[*]}  # 第二种写法

数组长度

使用#关键符号

${#array[@]}  # 第一种写法
${#array[*]}  # 第二种写法

shell数值运算和字符串处理—expr命令

expr 是一个用于数值运算和字符串处理的Bash内置命令。它可以执行加法、减法、乘法、除法以及字符串的比较和操作。expr 命令的一般语法如下:

expr 表达式

说明:

  • 用空格隔开每一项
  • 用反斜杠放在shell特定的字符(如乘法符号 *)前面(发现表达式运行错误时,可以试试转义)
  • 对包含空格和其他特殊字符的字符串要用引号括起来
    expr会在stdout中输出结果。如果为逻辑关系表达式,则结果为真时,stdout输出1,否则输出0。
  • expr的exit code:如果为逻辑关系表达式,则结果为真时,exit code为0,否则为1。
  • 在数值运算时,expr 命令支持整数运算,而不支持浮点数运算。

字符串表达式

  • length STRING:返回STRING的长度
  • index STRING CHARSET:
    查找任意单个字符在STRING中最前面的字符位置,下标从1开始。如果在STRING中完全不存在CHARSET中的字符,则返回0。注意这里CHARSET可以不止写一个字符,可以写一个字符串,这样会从第一个字符串开始寻找。直到找到
  • substr STRING POSITION LENGTH:
    返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITION或LENGTH为负数,0或非数值,则返回空字符串。
  • 例如:

    string="Hello"
    echo $(expr length "$string")    # 获取字符串长度,结果为 5
    echo $(expr substr "$string" 2 4)   # 获取字符串从第二个开始,长度为4的子串,结果为 "ello"
    echo $(expr index "$string" "l")    # 查找字符 "l" 在字符串中的位置,结果为 3
    #或者用``
    echo `expr length "$string"`  # 获取字符串长度,结果为 5
    echo `expr index "$string" l`  # # 查找字符 "l" 在字符串中的位置,结果为 3
    echo `expr substr "$string" 2 4`  # #获取字符串从第二个开始,长度为4的子串,结果为 "ello"

整数表达式

算术表达式优先级低于字符串表达式,高于逻辑关系表达式。

  • + -
    加减运算。两端参数会转换为整数,如果转换失败则报错。

  • * / %
    乘,除,取模运算。两端参数会转换为整数,如果转换失败则报错。
    乘法要加反斜杠转义

  • () 可以改变优先级,但需要用反斜杠转义

例:

a=3
b=4

echo `expr $a + $b`  # 输出7
echo `expr $a - $b`  # 输出-1
echo `expr $a \* $b`  # 输出12,*需要转义
echo `expr $a / $b`  # 输出0,整除
echo `expr $a % $b` # 输出3
echo `expr \( $a + 1 \) \* \( $b + 1 \)`  # 输出20,值为(a + 1) * (b + 1)

shell标准输入和输出

read命令

read命令用于从标准输入中读取单行数据。当读到文件结束符时,exit code为1,否则为0。

参数说明

-p: 后面可以接提示信息
-t:后面跟秒数,定义输入字符的等待时间,超过等待时间后会自动忽略此命令

echo命令

-e
-e 是一个常用的选项(或叫做参数),它用于在使用 echo 命令输出文本时启用转义。具体来说,-e 选项会使得 echo 命令解释特定的转义字符,如 \n 表示换行,\t 表示制表符等。

  • \n:换行
  • \c:不换行
  • \t:tab
    如果没有使用 -e 选项,echo 命令会将 \n等 当作普通字符,而不是解释为转义符。
    例:

    echo -e "ha\nhah"
    echo -e "Hello \c"
    echo "World"
    echo "dsads\tds"

    输出:

    ha
    hah
    Hello World
    dsads   ds

    printf命令

    格式化输出 和c++中语法基本类似
    格式:

    printf format [arguments...]

    例:

    
    printf "Hello, World!\n"   # 输出普通文本,带有换行符
    printf "The result is: %d\n" 42   # 输出整数,带有换行符
    name="John"
    age=30
    printf "Name: %s, Age: %d\n" "$name" "$age"  # 使用格式替换标记输出变量

printf "%-10s %5d\n" "Apple" 3 # 输出字符串左对齐宽度为10,整数右对齐宽度为5
printf "%10s %-5d\n" "Apple" 3 # 输出字符串右对齐宽度为10,整数左对齐宽度为5
printf "Value: %.2f\n" 3.14159 # 输出浮点数保留2位小数

输出:

Hello, World!
The result is: 42
Name: John, Age: 30
Apple 3
Apple 3
Value: 3.14

## shell中的test命令与判断符号[]
在Bash中,test 命令和 [...]是用于条件测试和判断的工具。它们用于检查条件的真假,并根据条件的结果来决定是否执行特定的代码块。test 命令和 [...] 可以完成相同的任务,但使用 [...] 更常见,因为它是内置的条件测试语法。

这两个命令的一般语法是:
``` bash
test condition
[ condition ]

这里注意

  • []内的每一项都要用空格隔开
  • 中括号内的变量,最好用双引号括起来
  • 中括号内的常数,最好用单或双引号括起来

这里的 condition 是要进行判断的条件表达式。条件表达式可以包含文件测试、字符串比较、数值比较等不同类型的条件。

文件测试

命令格式:

test -letter filename 或者[ -letter filename ]

  • -e 文件是否存在
  • -f 是否为文件
  • -d 是否为目录
  • -e 文件是否存在
  • -f 是否为文件
  • -d 是否为目录

数值比较

命令格式
test $变量 -letter $变量 或者[ "$变量" -letter "$变量" ]

  • -eq a是否等于b (equal)
  • -ne a是否不等于b (not equal)
  • -gt a是否大于b(greatthan)
  • -lt a是否小于b(lessthan)
  • -ge a是否大于等于b(great+equal)
  • -le a是否小于等于b(less+equal)

字符串比较

  • -z STRING 判断STRING是否为空,如果为空,则返回- true
  • -n STRING 判断STRING是否非空,如果非空,则返回- true(-n可以省略)
  • str1 == str2 判断str1是否等于str2
  • str1 != str2 判断str1是否不等于str2

多重条件判定

格式
test -r filename -letter -x filename或者[ -r "filename" -letter -x "filename" ]
其中:letter:

  • -a 两条件是否同时成立
  • -o 两条件是否至少一个成立
  • ! 取反。如 test ! -x file,当file不可执行时,返回true

shell判断语句

格式

if [ condition1 ]
then
     echo 11
elif [condition2]
then
    echo 22
else
    echo 其他
fi

例:

a=4
if [ "$a" -eq 1 ]
then
    echo ${a}等于1
elif [ "$a" -eq 2 ]
then
    echo ${a}等于2
elif [ "$a" -eq 3 ]
then
    echo ${a}等于3
else
    echo 其他
fi

结果输出其他

shell循环语句

for variable in list do....done格式

格式如下:

for variable in list
do
    # 执行循环中的代码块
done

这里的list可以换成 一些命令的stdout,例如ls、seq(seq 是一个用于生成序列的命令)以及大括号生成序列({1..10})等等,
例:

for file in `ls`
do
    echo $file
done
#输出当前目录下的所有文件名
for i in $(seq 1 10)
do
    echo $i
done
#输出1-10
for i in {a..z}
do
    echo $i
done
#输出a-z

for ((…;…;…)) do…done格式

bash中也支持像c++中那样的for循环格式,只不过要两层括号

for ((i=1; i<=10; i++))
do
    echo $i
done

while…do…done格式

例:

while read name
do
    echo $name
done

类比于 while(scanf("%d",&x)!=EOF)或者while(cin>>x)

until…do…done循环

until后边的条件为真时退出循环,和while是相反的

until condition
do
    语句1
    语句2
    ...
done

break和continue

和c++中没什么区别

shell中的函数

  • 一般语法:
    function_name() {
    # 函数体(命令或代码块)
    }

    或者

    function function_name {
    # 函数体(命令或代码块)
    }
  • 函数的输入参数
    在函数内,$1表示第一个输入参数,$2表示第二个输入参数,依此类推。
    注意:函数内的$0仍然是文件名,而不是函数名。
  • 函数内的局部变量
    可以在函数内定义局部变量,作用范围仅在当前函数内。

一般可以在递归函数中定义局部变量:local 变量名=变量值

注意事项:
但是值得注意的是,在其他我学过的语言中(目前就接触了js、c++、python)获得获取函数返回值都是以这个形式:x=function_name()并且这个返回值是任意的,就是可以返回指针、整形、字符型等等,但是bash中的返回值是返回的exit code,取值为0-255,0表示正常结束,返回其他的会报错,如果想像其他语言那样的用法的话,一般是将想要返回的值通过echo输出到stdout,然后通过$(function_name)获取stdout的值,同时,如果实在是想获得返回的exit code,也不能直接通过=赋值,需要通过$?来获取
例:
bash中的递归:计算阶乘

factorial() {
    if [ "$1" -eq 0 ]; then
        echo 1
    else
        local n="$1"
        ((n--))
        local sub_result=$(factorial "$n")
        echo $((sub_result * $1))
    fi
}

# 调用递归函数
echo $(factorial 5)
#输出:120

对比一下c++的阶乘:

int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1; // 递归终止条件,0和1的阶乘均为1
    } else {
        return n * factorial(n - 1); // 递归调用计算子问题的阶乘
    }
}

可以看到在bash中,是不能直接return的,需要我们先echo想要return的值,然后通过 $(factorial "$n") 来获取这个值
还有就是为什么只输出120不输出其他的值,明明每层都有echo,
这是因为只要在echo后边有一个$,这个$就会截掉stdout中的值,所以echo找不到stdout中的值而无法输出,
在递归往回调用的过程中上一层的echo会被这一层的local sub_result=$(factorial "$n")中的$截断而无法输出,而在最后一层的echo会被调用这个函数时的echo $(factorial 5)中的$截断,无法输出,同理,如果将echo去掉直接写factorial 5,也是可以输出120的,就是输出的最后一层的120而不是调用时的120.

shell中的exit命令

1.在Bash中,exit 是一个用于终止脚本执行的内置命令。它用于退出当前的Bash脚本或Shell会话,并返回一个介于0-255退出状态码,通常用于表示脚本的执行结果或状态,只有0表示成功。默认为0,这个状态码可以通过$?来获取
2.在子shell中使用 exit 命令将只会退出当前子shell,不会影响父shell。但在主脚本或交互式Shell中使用 exit 命令将终止整个脚本或Shell会话。
exit 与return 的区别
return和exit的共同之处都是返回exit code,区别是return结束当前函数,exit结束整个shell脚本

shell文件重定向

每个进程默认打开3个文件描述符:

  • stdin标准输入,从命令行读取数据,文件描述符为0
  • stdout标准输出,向命令行输出数据,文件描述符为1
  • stderr标准错误输出,向命令行输出数据,文件描述符为2
    可以用文件重定向将这三个文件重定向到其他文件中。
命令说明
command > filestdout 重定向到 file
command < filestdin 重定向到 file
command >> filestdout 以追加方式重定向到 file
command n> file将文件描述符 n 重定向到 file
command n>> file将文件描述符 n 以追加方式重定向到 file

例:

echo -e "Hello \c" > output.txt  # 将stdout重定向到output.txt中
echo "World" >> output.txt  # 将字符串追加到output.txt中

read str < output.txt  # 从output.txt中读取字符串

echo $str  # 输出结果:Hello World

shell引入外部脚本

类似 include、import等操作
语法格式:

. filename  # 注意点和文件名之间有一个空格

#或

source filename

source就相当于将filename执行了一遍,所以路径一定要正确,可以使用绝对路径

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇