Linux bash shell 编程笔记(基础篇)

文章目录

  • Part.I Introduction
    • Chap.I 传送门
    • Chap.II 基础知识
    • Chap.III 实操笔记
  • Part.II 程序设计控制结构
    • Chap.I if 分支程序
    • Chap.II case 分支结构
    • Chap.III for循环
    • Chap.IV while 和 until 循环语句
    • Chap.V 函数
    • Chap.VI 其他
  • Part.III 常用小功能实现
    • Chap.I 计算浮点数
    • Chap.II 脚本间函数的相互调取
    • Chap.III 判断变量类型
  • Part.IV 杂记

Part.I Introduction

Chap.I 传送门

  • Bash 和 Shell 的关系

Chap.II 基础知识

shell程序

  • shell程序是将用户键入的shell命令,按照控制结构组织到一个文本文件中,批量地交给shell执行。
  • shell程序通过shell命令解释器解释执行,不生成二进制可执行代码,类似于dos下的批处理文件(.bat)
  • shell命令解释器有多种,我电脑上有如下:
    Linux bash shell 编程笔记(基础篇)_第1张图片
  • shell程序又称shell脚本程序。不同的shell命令解释器所对应的shell脚本语法不完全相同。bash shell应用最为广泛。也就是说,bash是shell的一种,比较常用。实际上bash是borne again shell的缩写,它是shell的一种,Linux上默认采用的是bash。
  • shell脚本程序和其他高级语言程序类似,包括变量、函数、控制结构(顺序结构、选择结构、循环结构)。一般情况下脚本的第一行为:#!/bin/bash,它指定了随后行的shell命令解释器为bash shell

shell脚本执行流程

touch grep_test_file.sh     #st1 新建文件-编辑内容
chmod +x grep_test_file.sh  #st2 修改可执行权限
./grep_test_file.sh         #st3 执行

一些注意事项

  • 变量声明:var=value =两边不能有空格
  • 若引用'$var world'不能实现变量替换,要用强引用"$var world"
  • expr 3 + 2运算符与操作数之间需要至少一个空格
  • 乘号* 必须用单双引号或者反斜杠
  • expr、let只能对整数型数据进行运算,awk、bc可以实现浮点运算
  • let s=(2+3)*4 可以连续运算,不必有空格
  • <、 >、 &、 |等特殊符号要用双引号、单引号或者反斜杠
  • shell程序中,0表示为真,非0表示为假。
  • $?输出变量?的值,该特殊变量存储代码最后执行状态(退出码),执行成功则为0,否则为非0
  • 条件判断命令: test condition[ condition ],注意后者condition 两侧至少有一个空格。condition 可以是一个变量的值;文件的执行权限;某段代码的执行结果(执行成功为真0,不成功为假1);多个判断结果的逻辑运算。
  • 数值型比较时,只能整数比较。
  • 大于和小于顺序和sort命令所采用的不同,sort命令默认排序t,然而if语句比较T
  • echo $((2*3+6))这个是对空格没啥要求,而且也不需要反斜杠,但是注意要俩括号!!
  • [ $i -ge 0 -a $i -lt 5 -a $i -ne 2 -a $i -ne 3 ] 多个条件搞一起判断,注意是-a,而不是C中的&&!!
  • A=(-5 -3 -1 1 2 3 5) 数组,是空格,而不是,!!
  • 嫌一行代码太长,用\然后回车第二行接着写

${变量名}来引用变量(前提var ="hello"):

方式 实例 结果
${variable:-value} echo $var ${title:-"somebody"}! hello somebody!
${variable:+value} echo $var ${title:+"somebody"}! hello !
${variable:=value} echo $var ${title:="somebody"}! hello somebody!
${variable:?value} echo $var ${title1:?"titile1 is null or empty"}! bash: title1: titile1 is null or empty
${variable:offset[:length]} echo $var ${title:3:5}! hello ebody!

描述:

  • -:如果变量variable存在,则返回variable的值,否则返回值value
  • +:如果变量variable存在,则返回variable的值,否则返回空值
  • =:如果变量variable存在,则返回variable的值,否则先将value赋给变量variable,然后返回值value
  • ?:如果变量variable存在,则返回variable的值,否则将value送到标准错误输出显示并退出shell程序,这里value通常为一个错误提示消息
  • offset:其中offset和length为整数数字,中括号代表可选部分。此应用方式表示返回从变量variable的第(offset+1)个字符开始的、长度为length的子串。如果中括号内的部分省略,则返回其后所有子串。

Chap.III 实操笔记

下面的内容一行一信息点:

echo $BASH_VERSION #查看 BASH 版本
echo -e "\thello world\n" #转义字符发挥作用 \t 水平制表 \n换行 \v 竖直制表
man <command> #列出的所有使用方法
#这是注释
#命令 [选项] <参数1> <参数2>...
#大小写区分,一定要注意空格
# ’/‘表示根目录
#命令条之间用回车或分号分割
a="hello" / b=9   #变量命名,注意变量名和等号之间没有空格
echo "a is $a"    # a is hello  引用变量
------------------------------------------------
expr 3 + 2        #两个数相加,注意运算符与数字之间要有空格
expr 3 \* 2       
expr 3 '*' 2
expr 3 "*" 2      #计算两个数相乘,expr 3 * 2 是不对的
s=`expr 2 + 3`    #这个符号为英文输入法状态下Esc下面那个键
expr $s \* 4
expr `expr 2 + 3` \* 4
let s=(2+3)*4
echo $s
echo $(((2+3)*4))  #计算(2+3)*4
s1=3
s2=5
let s=s1\<s2  | s=s1"<"s2  | s=s1'<'s2
int_var1=250
int_var2=300
[ $int_var1 -eq $int_var2 ]
echo $?            #判断两个数,是否相等,返回1
# /dev/null 是一个不产生任何输出的特殊设备
line="Hello world!"
echo "length of the string 'line' is: ${#line}"  #计算字符串的长度

Part.II 程序设计控制结构

Chap.I if 分支程序

1.if 分支程序

#! /bin/bash
#if-判断一个路径是否正确
echo "input a directory, please!"
read dir_name
if cd $dir_name > /dev/null 2>&1;then
	echo "enter directory: $dir_name succeed"
else
	echo "enter directory: $dir_name failed"
fi

这是书上的例子,垃圾书,空格看不清,搞得我搞了好久才不报错。

# !/bin/bash
#an example script of if 这个可以判断对文件的权限,注意空格
clear
echo "input a file or directory name, please"
read path_name
if [  -d $path_name  ]
then
        echo "$path_name is a directory"
elif [  -f $path_name  ]
then
        echo "$path_name is a regular file"
        if [ -r $path_name ]
        then
                echo "$path_name is a readable file"
        fi
        if [ -w $path_name ] 
        then
                echo "$path_name is a writeable file"
        fi
        if [ -x $path_name ]
        then
                echo "$path_name is a executable file"
        fi
else
        echo "this script cannot get the file/directory($path_name)information!"
fi

关于判断条件

Attention!! 0 true 1 false
# 逻辑运算符
-a#condition1 -a condition2
-o#condition1 -o condition2
!# !condition
# 常用字符串属性条件判断
string1 = string2  #相等为真,否则为假
string1 != string2 #不等为真,否则为假
-z string #string长度为0返回真,否则返回假
-n string #string长度不为0返回真,否则返回假
string    #同 -n string
# 常用的整数关系条件判断
num1 -eq num2 #相等为真,否则为假
num1 -nq num2 #不等为真,否则为假
num1 -gt num2 #1大为真,否则为假
num1 -lt num2 #1小为真,否则为假
num1 -le num2 #1小等真,否则为假
num1 -ge num2 #1大等真,否则为假
# 常用的文件属性条件判断
-f fn #若fn存在且为普通文件为真,否则为假
-b fn #若fn存在且为块设备为真,否则为假
-e fn #若fn存在为真,否则为假
-d fn #若fn存在且为目录为真,否则为假
-r fn #若fn存在且可读为真,否则为假
-w fn #若fn存在且可写为真,否则为假
-x fn #若fn存在且可执行为真,否则为假
-O fn #若fn存在且被当前用户拥有为真,否则为假
-L fn #若fn存在且为符号链接为真,否则为假

Chap.II case 分支结构

2.case 分支结构

# !/bin/bash              解释器的路径
#a Simple shell script Example 
clear                     #清空终端,功能同Ctrl+L,类似于Matlab
echo "Are you like Linux?"    #回显,类似于C的“print”
read like_it              #从屏幕读入数据存到like_it变量中
case $like_it in          #根据like_it的内容做出不同反应,类似于C的switch-case,取值用$var
	y|Y|yes|Yes)echo "Linux is a good frind of us."
		;;           #")"前是情况,"|"是或,双分号表示,一个case的结束
	n|N|no|No)echo "Try it, and you will like it."
		;;
	*) echo "your answer is:$like_it"
		;;
	esac             #case倒写表示case结束。

Chap.III for循环

3.for循环

#! /bin/bash
#for 变量[in 列表]
#do
#  命令(通常用到循环变量)
#done
clear
for os in LinuX Windows UNIX
do 
	echo "Operation System is: $os"
done
-------------
Operation System is: LinuX
Operation System is: Windows
Operation System is: UNIX
++++++++++++++++++++++++++++++++++++++
#!/bin/bash
for city in Wuhan Changsha "Shang hai"
do
	echo "the current city is ${city}"
done
---------
the current city is Wuhan
the current city is Changsha
the current city is Shang hai
++++++++++++++++++++++++++++++++++++++
#!/bin/bash
A=(1.1 1.2 1.3 1.4)
echo "number of elements:${#A[@]}"
for city in ${A[@]}
do
	echo "${city}"
done
echo "--------------"
for str in ${A[*]}
do
	echo "${str}"
done
echo "++++++++++++++"
seq ${#A[@]}
for i in `seq ${#A[@]}`
do
	let n=i-1
	echo "${A[${n}]}"
done
++++++++++++++++++++++++++++++++++++++
number of elements:4
1.1
1.2
1.3
1.4
--------------
1.1
1.2
1.3
1.4
++++++++++++++
1
2
3
4
1.1
1.2
1.3
1.4

Chap.IV while 和 until 循环语句

4.while 和 until 循环语句

#! /bin/bash
#while
clear
loop=0
while [ $loop -ne 10 ]
do
        let loop=$loop+1
        echo "current value of loop id:$loop"
done
# !/bin/bash
#until
clear
loop=0
until [ $loop -eq 10 ]
do
        let loop=$loop+1
        echo "current value of loop id:$loop"
done
------------------- output ------------------------
current value of loop id:1
current value of loop id:2
current value of loop id:3
current value of loop id:4
current value of loop id:5
current value of loop id:6
current value of loop id:7
current value of loop id:8
current value of loop id:9
current value of loop id:10

这俩区别不大,只有判断条件不同。字面意思理解:whilexx时,满足条件xx,执行;until 直到xx,当不满足xx时,执行。

Chap.V 函数

5.函数

# !/bin/bash
#function 格式
# [function] 函数名()
#{
#   命令
#}
#调用:函数名[ 参数1 参数2 ... 参数n]
function demo_fun()
{
	echo "Your commond is:$0 $*"
	echo "Number of Parameters(\$#)is:$#"
	echo "Script filename(\$0)is:$0"
	echo "Parameters(\$*)is:$*"
	echo "Parameters(\$@)is:$@"
	count=1
	for param in $@
	do
		echo "Paramters(\$$count)is:$param"
		let count=$count+1
	done
}
clear
demo_fun $@
=========
input:
./a.sh 1 2 3 4
-------------
output:
Your commond is:./a.sh 1 2 3 4
Number of Parameters($#)is:4
Script filename($0)is:./a.sh
Parameters($*)is:1 2 3 4
Parameters($@)is:1 2 3 4
Paramters($1)is:1
Paramters($2)is:2
Paramters($3)is:3
Paramters($4)is:4
+++++++++++++++++++++++++
上面的for循环可以替换为:
while [ -n "$1" ]
do
    echo "Parametrs(\$$count)is:$1"
    let count=$count+1
    shift
done

局部变量与全局变量
局部变量在函数体内有效,全局变量在整个脚本自该变量以下均有效。
• 函数体内没有明确指明该变量是局部变量时,其为全局变量。函数体内变量的值的改变会影响脚本中同名变量的值。
• 定义局部变量格式: local var。函数体内明确定义了局部变量,则该变量仅在函数体内有效,变量值的改变不影响脚本中同名变量的值。

参数传递
• shell脚本中的定义的函数没有形参列表,输入参数通过特殊变量$1,$2,…进行传递。
• 特殊变量: $@ shell参数列表, $*也是shell的参数列表,但参数列表作为一个整体。
${#array[@]}或者${#array[*]}获取数组元素个数
$#传递给shell脚本的参数的个数

#!/bin/bash
test(){
local newarray
newarray=`echo "$@"`
echo "The new array value is: ${newarray[*]}"
for str in ${newarray[*]}
do
echo "${str}"
done
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[@]}"
echo "number of elements: ${#myarray[@]}"
echo "number of elements: ${#myarray[*]}"
test ${myarray[*]}
---------------------------------------------
The original array is 1 2 3 4 5
number of elements: 5
number of elements: 5
The new array value is: 1 2 3 4 5
1
2
3
4
5

参数移动

  • bash shell 工具箱中另一个工具就是shift命令。它能够用来操作命令行参数。 shift命令会根据它们的相对位置来移动命令行参数。
  • 默认情况下, shift命令将每个参数向量向左移动一个位置。例如,变量$3会移动到变量$2的位置,变量$2会移动到变量$1的位置,变量$1会被删除(注意:变量$0的值是脚本文件名称, 不会改变)
#!/bin/bash
while [ -n "$1" ]
do
echo -e "\033[31;1m$@\033[0m"
echo "When invoking 'shift' each time, the command list is"
shift
done
--------------------------------------
./test.sh Luo yu lu Whu University China
Luo yu lu Whu University China
When invoking 'shift' each time, the command list is
yu lu Whu University China
When invoking 'shift' each time, the command list is
lu Whu University China
When invoking 'shift' each time, the command list is
Whu University China
When invoking 'shift' each time, the command list is
University China
When invoking 'shift' each time, the command list is
China
When invoking 'shift' each time, the command list is

Chap.VI 其他

C可执行程序的环境变量设置
• 通常可执行程序可以在编译生成该程序的目录下运行,一旦换一个目录运行该可执行程序,会出现“ command not found”的错误,原因在于在其他目录系统无法搜索到该可执行程序的路径。
• 因此,要定义一个关于该可执行程序存放的路径的变量,这个变量就是环境变量。
定义完了环境变量,并导入环境变量后,系统可以自动按照定义好的变量值的路径,去查找该可执行程序。因而可以在任意目录运行该程序。
• 设置环境变量的几种方式

  1. 终端设置(只对当前shell有效)
    $PATH="$PATH":/NEW_PATH
  2. 修改 /etc/profile 文件。所有用户的shell都有权使用这个环境变量, 可能会给系统带来安全性问题。
    • 在/etc/profile的最下面添加: export PATH="$PATH:NEW_PATH"
  3. 修改.bashrc文件或.bash_profile(当前用户有效)
    export PATH="$PATH:NEW_PATH”
    • 注意NEW_PATH是一个完整的路径!!

• 第三种方法设置环境变量的步骤:

#to add local environment variables, five stepsshould be performed:#step1:
touch ~/.bash_profile
#this is used to create a local customized environment variable file#step2:
vi ~/.bash_profile
• #step3:  input: 
export PATH="$PATH:XXXX"
#XXXX is the absolute directory of an executable program#step4: 
source ~/.bash_profile
#this is used to activate the newly defined environmen variable.#step5: 
echo $PATH
#to check if the newly defined variable is correctly set.

环境变量的作用:在当前目录下运行其他目录(当前目录没有的)下的可执行程序。

Part.III 常用小功能实现

Chap.I 计算浮点数

这块参考:https://www.jb51.net/article/50642.htm

其实,Shell(这里是Bash)本身不具备处理浮点计算的能力,但是可以使用“bc”这个高精度的计算器工具来帮助,另外,也可以在Bash中调用“awk”脚本来处理浮点运算。
1. 用bc来处理计算(包括整型和浮点计算)

bc – An arbitrary precision calculator language
(1). 通常在Bash脚本中使用bc的范例格式为:

variable=$(echo “OPTIONS; OPERATIONS” | bc [options])
即:echo “[选项];操作” | bc [选项]

(2). 在下面的脚本中,提到在第一个选项中,“scale”变量表示输出中小数点后的精度,可以用于控制计算结果的精度;“ibase”和“obase”分别表示输入和输出数据的进制,可以用于数值进制的转换。
(3). 浮点数的比较,如“if [ $(echo "$big > $small" | bc) -eq 1 ]”,将一个逻辑判断式用管道传给bc。如果结果为真则输出1,否则输出0,然后就可以利用这个结果进行进一步的操作了。
(4). bc本来是用一个文件作为输入进行计算的(后面也有演示),所以可以将很复杂的计算写到文件中,然后让bc工具去处理到处计算结果。
注意一下:在使用除法运算符/时,要想保留小数,需要自己设置scale,否则默认时scale,小数点后时0位。
2. 使用awk来处理浮点计算和浮点数比较
不解释过多了,写了示例脚本如下,看懂了这个就会知道怎么处理浮点计算和浮点数比较了。

#!/bin/bash
# author: Jay 
# some examples for playing with floating point number.

# basic usage of 'bc' tool in Bash.
a=3.33
b=3.3
c=$(echo "$a + $b" | bc)
d=$(echo "$a * $b" | bc)
e=$(echo "scale=5; $a / $b" | bc)
echo "c=a+b=$a+$b=$c"
echo "d=a*b=$a*$b=$d"
echo "e=a/b=$a/$b=$e"

# "-l" parameter for 'bc' means using math library.
pi=$(echo "scale=10; 4*a(1)" | bc -l)
s=$(echo "s($pi/6)" | bc -l)
echo "pi=$pi"
echo "s=sin(pi/6)=$s"

# use more options of 'bc' tool
r=$(echo 'ibase=10;obase=2; 15+16' | bc)
echo "binary of (15+16) is $r"

# comparison for floating point numbers using 'bc'
big=100
small=99.9
if [ $(echo "$big > $small" | bc) -eq 1 ]; then
        echo "$big is bigger than $small"
fi

# deal with floating point numbers with 'awk' language
echo $(awk -v x=10 -v y=2.5 'BEGIN {printf "10/2.5=%.2f\n",x/y}')
v=$(echo $big $small | awk '{ printf "%0.8f\n" ,$1/$2}')
echo "$big / $small = $v"

echo $big $small | awk '{if($1>$2) {printf"%f > %f\n",$1,$2} else {printf"%f <%f\n",$1,$2}}'

执行的结果如下:

master@jay-linux:~/workspace/mygit/shell/sh2012$ ./floating-point.sh
c=a+b=3.33+3.3=6.63
d=a*b=3.33*3.3=10.98
e=a/b=3.33/3.3=1.00909
pi=3.1415926532
s=sin(pi/6)=.49999999994373819220
binary of (15+16) is 11111
100 is bigger than 99.9
10/2.5=4.00
100 / 99.9 = 1.00100100
100.000000 > 99.900000

另外,bc处理一个文件中的计算逻辑,演示如下:

master@jay-linux:~/workspace/mygit/shell/sh2012$ cat temp.bc
3+8
3/8
scale=2; 3/8

master@jay-linux:~/workspace/mygit/shell/sh2012$ bc -q temp.bc
11
0
.37

bc是强大的工具,请“man bc”查看详情;同样,请“man awk”。


现总结如下:

a=3.33
b=3.3
c=$(echo "$a + $b" | bc)
echo $(echo "20.12/12" | bc)  #这行返回1, 想要得到浮点的,得用scale选项
echo $(echo "scale=5; $a/$b" |bc)  #这行返回1.00909
echo "sin(pi/6)=$(echo "scale=10; s(3.1415926/6)" | bc -l)"
# 这行返回 sin(pi/6)=.4999999922   
#-l parameter for 'bc' means using math library.
echo | awk '{print 3.2/2.0}' #这行返回1.6

Chap.II 脚本间函数的相互调取

令我受益匪浅的文章

https://jingyan.baidu.com/article/95c9d20d854779ad4e756190.html

一句话,函数任你调用:

source a.sh  #  . ./a.sh(有空格)

这句话 a.sh会被执行一遍,然后a.sh里面的函数就可以随便调用了。

Chap.III 判断变量类型

大佬文章:https://blog.csdn.net/qq_17528659/article/details/87183363

里面的小函数如下:

#!/usr/bin/env bash
function check(){
    local a="$1"
    printf "%d" "$a" &>/dev/null && echo "integer" && return
    printf "%d" "$(echo $a|sed 's/^[+-]\?0\+//')" &>/dev/null && echo "integer" && return
    printf "%f" "$a" &>/dev/null && echo "number" && return
    [ ${#a} -eq 1 ] && echo "char" && return
    echo "string"
}

调用:

echo ". is" $(check ".")
echo "1 is" $(check "1")
echo ".1 is" $(check ".1")
echo "1. is" $(check "1.")
echo "1234 is" $(check "1234")
echo "1.234 is" $(check "1.234")
echo "1.2.3.4 is" $(check "1.2.3.4")
echo "a1234 is" $(check "a1234")
echo "abc is" $(check "abc")
echo "a is" $(check "a")
echo "1e+2" $(check "1e+2")
echo "1.e+2" $(check "1e+2")
echo ".1e+2" $(check "1e+2")
echo "-1" $(check "-1")
echo "-1.2" $(check "-1.2")
echo "-a" $(check "-a")
echo "0x1f" $(check "0x1f")
echo "0x1H" $(check "0x1H")
echo "0333" $(check "0333")
echo "0999" $(check "0999")
echo "+003" $(check "+003")
echo "+003.3" $(check "+003.3")
-----------------------------
. is char
1 is integer
.1 is number
1. is number
1234 is integer
1.234 is number
1.2.3.4 is string
a1234 is string
abc is string
a is char
1e+2 number
1.e+2 number
.1e+2 number
-1 integer
-1.2 number
-a string
0x1f integer
0x1H string
0333 integer
0999 integer
+003 integer
+003.3 number

Part.IV 杂记

typeset -u tmp;tmp='lowcase';echo "$tmp"  #小写转大写
typeset -l tmp;tmp='UPCASE';echo "$tmp"   #大写转小写
file='I am OHANLON'
echo ${file:0:1}             #从0开始索引
echo ${file:3:${#file}-4}    #含3,取后面的长度-4个字符
echo ${#file}                #字符串的长度
echo ${#array[@]}            #数组的长度
array=(${string//,/ })       #将以,分割的字符串转化为字符数组
array=(`echo $string | tr ',' ' '` ) 
#这句的作用同上,它是先将,替换为空格,然后转化为字符数组
string1=`echo $string | tr ',' ' '`
string1=$(echo $string | tr ',' ' ')
#将string中的,转化为空格并保存到string1中
array=($string)              #以默认分隔符(空格)将字符串变成数组
OLD_IFS="$IFS"
IFS=","                      #改变默认分隔符为,
array=($string)
IFS="$OLD_IFS"               #分割完成之后再变回来。
a=$(printf "%05d" 123)       #a=00123 位数不够前补0
grep -c str file			 # grep -c 返回 file中,与str匹配的行数

你可能感兴趣的:(Linux,Note,shell,linux)