【shell】bash script基础入门及例子(附代码)持续更新

发布时间:2024年01月06日

A Bash script is a plain text file which contains a series of commands.

Anything you can run normally on the command line can be put into a script

————https://ryanstutorials.net/bash-scripting-tutorial/bash-script.php

Reference

教程:一篇教会你写90%的shell脚本 - 知乎 (zhihu.com)

教程:ryanstutorials.net/bash-scripting-tutorial

教程:linuxconfig.org/bash-scripting-tutorial

教程:Bash Reference Manual

简介

Bash(Bourne Again SHell)是一种广泛使用的Unix shell和命令语言。

Bash(Bourne Again SHell)是一种命令语言,同时也是一个命令行界面。它是一个解释器,解释并执行输入到命令行的命令。

Bash 作为Linux和macOS的默认shell,Bash在自动化脚本、任务调度、系统管理等方面非常流行。

符号

$( ) :【命令替换符】在shell script中执行shell命令的方式是通过$( )参数创建一个subshell。用来重组命令行,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。

(Reference: The best way to execute a separate shell command inside of a Bash script is by creating a new subshell through the $( ) syntax. )

$STRING:获取变量,使用一个定义过的变量

${#STRING} :获取字符串长度,在${}中使用#获取长度

${STRING:1:4} 截取字符串

echo ${#name}; # 输出为4

分号,Shell 会按顺序执行每个命令,不论前一个命令的执行结果如何。

dirname $0 #  获取脚本文件所在的目录
$(cd $(dirname $0); pwd) #获取shell脚本所在目录的绝对路径

注释

  • 单行注释: #
  • 多行注释: :<

特殊变量

Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数 - davygeek - 博客园 (cnblogs.com)

$0	# 当前脚本的文件名
$n	# 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$#	# 传递给脚本或函数的参数个数。

$*	# 传递给脚本或函数的所有参数(将参数作为一个整体)。
$@	# 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同(会将参数分开),下面将会讲到。

$?	# 上个命令的退出状态,或函数的返回值。(大部分命令执行成功会返回 0,失败返回 1。)
$$	# 当前Shell的进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。
双引号(" ")包含时
"$*" 会将所有的参数作为整体,以"$1 $2$n"的形式输出所有参数;
"$@" 会将所有的参数各个分开,以"$1" "$2""$n" 的形式输出所有参数。

运算符

数字

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

# 下面假定变量 a 为 10,变量 b 为 20

-eq # 相等 [ $a -eq $b ] 返回 false。
-ne # 不相等  [ $a -ne $b ] 返回 true。
-gt # 大于  [ $a -gt $b ] 返回 false。
-lt # 小于  [ $a -lt $b ] 返回 true。
-ge # 大于等于  [ $a -ge $b ] 返回 false。
-le # 小于等于  [ $a -le $b ] 返回 true。

字符串

# 假定变量 a 为 "abc",变量 b 为 "efg":

=  # 相等  [ $a = $b ] 返回 false。
!= # 不相等  [ $a != $b ] 返回 true。

-z # 字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n # 字符串长度是否为0,不为0返回 true。 [ -n "$a" ] 返回 true。
$  # 字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

布尔

! :非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o :或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a :与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算

# 假定变量 a 为 10,变量 b 为 20:
&& # 逻辑的 AND 
[[ $a -lt 100 && $b -gt 100 ]] # 返回 false

|| # 逻辑的 OR 
[[ $a -lt 100 || $b -gt 100 ]]  # 返回 true
# 只有在 && 左边的命令返回真(命令返回值 $? == 0),&& 右边的命令才会被执行。
command1 && command2 && command3 ...

# 只有在 || 左边的命令返回假(命令返回值 $? == 1),|| 右边的命令才会被执行。
command1 || command2

文件

-b file :检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file :检测文件是否是字符设备文件,如果是,则返回 true。 
-d file :检测文件是否是目录,如果是,则返回 true。 
-f file :检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
-g file :检测文件是否设置了 SGID 位,如果是,则返回 true。
-k file :检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 
-p file :检测文件是否是有名管道,如果是,则返回 true。
-u file :检测文件是否设置了 SUID 位,如果是,则返回 true。 
-r file :检测文件是否可读,如果是,则返回 true。
-w file :检测文件是否可写,如果是,则返回 true。 
-x file :检测文件是否可执行,如果是,则返回 true。
-s file :检测文件是否为空(文件大小是否大于0),不为空返回 true。
-e file :检测文件(包括目录)是否存在,如果是,则返回 true。

命令替换符

$( ) :【命令替换符】在shell script中执行shell命令的方式是通过$( )参数创建一个subshell。用来重组命令行,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。

echo $(ls /etc) 

算术运算

  1. $[ ] : 加减乘除,不必添加空格
  2. $(( )) :加减乘除等,不必添加空格

逻辑判断

  1. [[ ]]:中括号旁边和运算符两边必须添加空格 (字符串验证时,推荐使用)
  2. (()) : 中括号旁边和运算符两边必须添加空格 (数字验证时,推荐使用)

变量

变量类型

  • 局部变量:在脚本或命令中定义,仅在当前shell实例中有效
  • 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量
  • shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量

1、变量定义

创建普通变量: name="test"=两边不可有空格)
变量重新赋值: name="new_test" (将原值覆盖)

创建局部变量: local name="test" (只可函数体中使用的,使用local修饰的变量在函数体外无法访问,并且local只能在函数体内使用)
创建只读变量: name="only_read" -> readonly name (使用readonly标识后的变量,不可被修改)

2、变量由结果赋值 $( )

#反引号`` 或者 $()
variable=`command`
variable=$(command) # 更推荐这种方式

#e.g
log=`cat log.txt`
log=$(cat log.txt) 

3、变量引用

# $ :使用一个定义过的变量,只要在变量名前面加美元符号$即可
name="严长生"
echo $name

# ${ } :变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界 (推荐使用大括号版)
echo ${name}

双引号 vs. 单引号

  • 单引号:原样输出,变量无效
  • 双引号:变量可输出,变量有效
BASH_VAR="Bash Script"
echo '$BASH_VAR  "$BASH_VAR"'
# bash result
# $BASH_VAR "$BASH_VAR"

参考: shell的时间参数含义表示——%y%m%d/%H:%M:%S_uj_m的博客-CSDN博客_%y%m%d%h%m%s

loacl_time=$(data+"%Y%m%d %H:%M:%S")

4、unset 删除变量

unset name; (删除之后不可访问,删除不掉只读变量)

拼接字符串

name="this is"" my name"; 
name="this is my name"; 
name="this" is "my name" 
# 以上等效

Array

bash只支持一维数组,不支持多维数组

${array_name[@]} 数组所有元素

${#array_name[@]} 数组元素个数

  • Declare simple bash array

This example declares an array with four elements.

#!/bin/bash
#Declare array with 4 elements
ARRAY=( 'Debian Linux' 'Redhat Linux' Ubuntu Linux )
# get number of elements in the array
ELEMENTS=${#ARRAY[@]}

# echo each element in array 
# for loop
for (( i=0;i<$ELEMENTS;i++)); do
    echo ${ARRAY[${i}]}
done 

echo ${ARRAY[@]}

重定向

文件描述符:STDINSTDOUTSTDERR

  • 标准输入(STDIN):文件描述符为0。通常用于读取输入,如键盘输入。
  • 标准输出(STDOUT):文件描述符为1。用于输出数据,如屏幕显示。
  • 标准错误(STDERR):文件描述符为2。用于输出错误信息,通常也是显示在屏幕上,但可以被分开处理。

文件描述符是一个在Unix和类Unix操作系统(如Linux)中用于访问和管理文件的抽象概念。

操作符:1>&2:

command > file	# 将输出重定向到 file (指定文件作为命令的输出设备)。
command < file	# 将输入重定向到 file (指定文件作为命令的输入设备)。
command >> file	# 将输出以追加的方式重定向到 file。

Input 输入

https://ryanstutorials.net/bash-scripting-tutorial/bash-input.php

  • read命令接收标准输入(键盘)的输入,或者其他文件描述符的输入,并将其保存在一个变量中。
  • read 命令用于一个一个词组地接收输入的参数,每个词组需要使用空格进行分隔;
introduction.sh
#!/bin/bash
# Ask the user for their name
echo Hello, who am I talking to?
read varname
echo It\'s nice to meet you $varname
bash ./introduction.sh
Hello, who am I talking to?
Ryan
It's nice to meet you Ryan

参数

  • -p 输入提示文字
  • -s which makes the input silent.
# 从键盘输入
read firstStr secondStr
echo "第一个参数:${firstStr}  第二个参数:${secondStr}"

# 从键盘输入(-p)
read -p "请输入一段文字:" firstStr

# 从文件输入
read PID < $FILE 

# 读文件
# cat 命令的输出作为read命令的输入,read读到的值放在line中 
cat file| while read line
do
	echo "$line"
done
exit 0

Arithmetic

https://ryanstutorials.net/bash-scripting-tutorial/bash-arithmetic.php

expr item1 operator item2  
# e.g a=$( expr 4 + 5 )

$(( expression )) 
#e.g a=$(( 4 + 5 ))

${#variable} 
# Length of a Variable

IF 流程控制

概览

  • if
  • if-else
  • is-elif-else-fi
  • boolean operations
  • Case Statements

https://ryanstutorials.net/bash-scripting-tutorial/bash-if-statements.php

IF

sh的流程控制不可为空,即if或者else的大括号中无任何语句

if [ <some test> ];then
	<commands>
fi

if [ <some test> ]
then
<commands>
fi


if [ <some test> ];then
	<commands>
else
	<other commands>
fi

for

# for in do done
for var in item1 item2 ... itemN do command1 command2 ... commandN done

while

# while do done (condition可以没有)
while condition do command done

untile

until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。

# until do done 
until condition do command done

跳出循环

break

continue

case in

Shell case in语句详解 (biancheng.net)

case <variable> in
    <pattern 1>)
        <commands>
        ;;
    <pattern 2>)
        <other commands>
        ;;
esac

Loops 循环

概览

  • While Loops
  • Until Loops
  • For Loops
  • Break and Continue
  • select do done

https://ryanstutorials.net/bash-scripting-tutorial/bash-loops.php

while [ <some test> ];do
	<commands>
done
until [ <some test> ];do
	<commands>
done
for var in <list>;do
	<commands>
done

# e.g 
for f in $( ls /var/ ); do echo $f; done 

Functions 函数

Variable Scope(By default a variable is global. )

Overriding Commands

https://ryanstutorials.net/bash-scripting-tutorial/bash-functions.php

function_name () {
	<commands>
}

[ function ] funname() {
	action; 
	[return int;]
    }
  • return可存在也可不存在,如果不加return , 则默认最后一条语句的执行状态所为函数执行状态的返回值,(即 执行成功$?为0,否则不为0)

  • function可存在也可不存在

1、函数定义及调用

# 定义
name() {
    statements
    [return value]
}

# 调用
name param1 param2 param3

2、传递参数

给定的参数以$1,$2,$3,…$n的形式访问,对应于函数名后参数的位置。

# e.g
count_num(){
	result = $1+$2
	echo "result is :$result"
}
count_num 5 6

3、变量的作用域

全局变量定义为可以在脚本内的任意位置访问的变量,而不管它的范围如何。

默认情况下,所有变量都定义为全局变量,即使它们在函数内部声明也是如此。还可以将变量创建为局部变量。可以使用local关键字在函数体内声明局部变量。

#!/bin/bash  

v1='A'  
v2='B'  

my_var () {  
    local v1='C'  
    v2='D'  
    echo "Inside Function"  
    echo "v1 is $v1."  
    echo "v2 is $v2."  
}  


my_var  
echo "v1 is $v1."   
echo "v2 is $v2."
# v1 is A
# v2 is D

4、e.g

count_num(){
	let "result = $1+$2" # or result=$(($1+$2))
	echo "result is :$result"
}
count_num 5 6
# result is :11

User Interface

其它命令

printf

printf 命令模仿 C 程序库(library)里的 printf() 程序。

和echo不一样,它不会在最后自动加上换行,需要写入命令中。

# e.g.
printf "Hello, world/n"

a='hello world'
echo "%s" $a     # 结果 %s
printf "%s\n" $a # 结果 Hello, world

echo 输出

用于字符串/变量的输出

# 脚本内部变量
#! /bin/sh
time=$(date) 
echo "time is ${time}"

# 等同于
echo `date`

输出到文件中

echo "time is `date`" > filename
echo "time is `date`" >> filename

长句换行

\:在脚本执行过程中还是当做一行一个语句执行,不同于enter直接换行

eval

当我们在命令行前加上eval时,shell就会在执行命令之前扫描它两次.eval命令将首先会先扫描命令行进行所有的置换,然后再执行该命令。

foo=10
x=foo
y='$'$x
echo $y
# $foo
eval y='$'$x
echo $y
$10

shell 启用程序

1、shell脚本启动python执行.py脚本

# start_py.sh
conda activate py37
cd /usr/test/
python main.py

# 上述也可写为
/usr/local/bin/python3.7 /usr/test/main.py

# 执行
source start_py.sh

注意:执行shell脚本文件时,一定是source,不能是bash/sh,其原因是 source启动的shell脚本,是在父进程中继续运行的。而后面的3个启动方法,是新建子进程运行的。

2、shell脚本启动gunicorn,执行gunicorn命令

gunicorn+shell脚本+cron让项目自动发布 | 纸镜 (moshiwei.github.io)

#!/bin/bash
/home/mirror/djangoenv/bin/gunicorn --bind unix:/tmp/mirrorgo.top.socket blogproject.wsgi:application

shell 参数传递

如何传递和解析Linux Bash脚本参数和参数-CSDN博客

1、传递参数

$n 读取参数,n代表第几个参数

bash xx.sh param1 param2 .. 读取参数,n代表第几个参数, 有空格加引号"xx xx "

# !/bin/bash
# my.sh 脚本(读取参数)
echo $1 $2 $3

# 执行(传参)
bash my.sh hello1 hello2 "hello3 hello4"

# 输出
hello1 hello2 hello3 hello4

2、检测命令行参数

#!/bin/bash
if [ "$1" != "" ]; then
   echo $1
else
    echo "empty"
fi

3、赋值令行参数

#!/bin/bash 
salute=$1 
name=$2 
 
echo $salute $name

4、getopt 处理参数

# abcd 分别代表四个选项,后面带有冒号的表示选项需要参数值。
GETOPTOUT=`getopt ab:c:d "$@"`  
    set -- $GETOPTOUT 
    while ...

# 执行
./proxychains4.sh -a -b t2 -c t3 -d

标准模板

ARGV=($(getopt -o 短选项1[:]短选项2[:]...[:]短选项n -l 长选项1,长选项2,...,长选项n -- "$@"))
eval set -- "$ARGV"
while true
do
case "$1" in
    -短选项1|--长选项1)
        process
        shift
        ;;
    -短选项2|--长选项2)
        # 获取选项
        opt = $2
        process
        shift 2
        ;;
    
    ... ...

    -短选项3|--长选项3)
        process
        ;;
    --)
        break
        ;;
esac
done

扩展

Linux提供了多种自动化工具,主要可以分为以下几类:

  1. 脚本语言:如 Bash、Python、Perl。这些都是常用的脚本语言,可以用于编写自动化脚本来完成各种任务。
  2. 配置管理工具:例如 Ansible、Puppet、Chef。这些工具用于自动化管理多台机器的配置和部署。
  3. 任务调度器:如 Cron、At。这些工具可以在预定时间自动执行任务。
  4. 编译和部署工具:例如 Make、CMake、Jenkins。这些工具主要用于软件开发中的自动化编译和部署。
  5. 监控和日志管理工具:如 Nagios、Zabbix、ELK(Elasticsearch, Logstash, Kibana)。这些工具可以帮助监控系统状态和管理日志。
  6. 容器和虚拟化工具:例如 Docker、Kubernetes、Vagrant。这些工具用于自动化容器的部署和管理。
  7. 版本控制系统:如 Git、Subversion。这些系统在软件开发中用于代码的版本控制,也常常与其他自动化工具结合使用。

Bash语言 vs. Linux命令行

Bash语言是在Linux命令行界面中使用的一种脚本语言,具有丰富的编程特性,适用于复杂任务的自动化。

Linux命令行是与系统交互的接口,用于执行单个命令或简单的命令序列。

当你在Linux命令行中输入命令时,通常是Bash或类似shell(如Zsh或Fish)在解释和执行这些命令。因此,Linux命令行默认支持Bash语言,你可以在命令行中直接编写和执行Bash脚本或命令。在Bash脚本中,你可以直接使用Linux命令行中的所有命令。

在Unix和类Unix系统中,shell是一个命令行解释器,提供与操作系统交互的用户界面。

Reference

相关:

Linux自动化工具和技能:An Introduction to Linux Automation, Tools and Techniques - Linux Tutorials - Learn Linux Configuration

文章来源:https://blog.csdn.net/qq_39317214/article/details/135427810
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。