Linux系统编程(2)—— shell编程基础

SHELL编程基础

shell 是一种脚本语言(解释型语言),需要解释器来执行。

Linux上的常见shell解释器有bash,zsh等

以下环境为bash

第一个shell脚本

#!/bin/bash#!”   :  告诉系统这个脚本需要什么解释器
echo 'Hello World!'

运行方式:

1.  bash first.sh

2.  chmod +x first.sh
    ./first.sh

变量与局部变量

定义变量与使用变量

  • 定义变量时,变量名不加美元符号($),如 : ABC=“123”
  • 变量名的命名须遵循如下规则:
    • 首个字符必须为字母(a-z,A-Z)
    • 中间不能有空格,可以使用下划线(_)
    • 不能使用标点符号
    • 不能使用 bash 里的关键字
  • 使用变量
    • 使用一个定义过的变量,只要在变量名前面加美元符号($)即可
A ="123"
echo $A
echo ${A}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。

变量类型

运行 shell 时,会同时存在三种变量 :

1) 局部变量

局部变量在脚本或命令中定义,仅在当前 shell 实例中有效,其他 shell 启动的程序不能访
问局部变量。

2) 环境变量

所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其
正常运行。必要的时候 shell 脚本也可以定义环境变量。

3) shell 变量

shell 变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分
是局部变量,这些变量保证了 shell 的正常运行。

Shell特殊变量

前面已经讲到,变量名只能包含数字、字母和下划线,因为某些包含其他字符的变量有特殊
含义,这样的变量被称为特殊变量。

特殊变量含义
$0当前脚本文件名
$n传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。
$@传递给脚本或函数的所有参数。
$?判断上条指令是否成功,0为成功, 非零为不成功
$$当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。
$!上一个指令的PID

变量,参数展开

${parameter:-word}      如果变量未定义,则表达式的值为word
用于对脚本的可靠性判断:
	Dir=/home/xxxxx/mnt2
	find ${Dir} -name "*.gz" -type f | xargs rm -rf
	如果某一时刻,Dir目录为空时,
	rm -rf ${Dir}/*  这时就出大问题
	所以先判断一下变量有没有被定义,相当于在未定义时,
	将${Dir}这个表达式的返回值设为默认值,此时Dir仍为空。
	
${parameter:=word}      如果变量未定义,则设置变量的值为word,返回表达式的值也是word
	
${parameter:?word}      检测变量是否存在,不存在返回word,用于捕捉由于变量未定义而导致的错误并退出程序
	find ${Dir:?"Not Found"} -name "*.gz" -type f | xargs rm -rf
	写成这样就比较安全
${parameter:+word}      如果变量已经定义,返回word

${!prefix*}   ${!prefic@}  prefix开头的所有变量的名字 

字符串展开

${#parameter}                 输出字符串长度
${parameter:offset}           从第offset字符开始截取,从0开始计数
${parameter:offset:length}    从第offset字符开始截取,取length长度
以下用的很少
${parameter#pattern}          从头删除最短匹配 
${parameter##pattern}         从头删除最长匹配
${parameter%pattern}          从尾删除最短匹配
${parameter%%pattern}         从尾删除最长匹配

字符串匹配与替换

${parameter/pattern/string}   第一个匹配被替换
${parameter//pattern/string}  第一个匹配被替换
${parameter#pattern/string}   字符串开头的替换
${parameter%pattern/string}   字符串结尾的替换
${parameter,,}  ${parameter^^}全部替换成小写,大写
${parameter,}  ${parameter^}  首字母替换成小写,大写

输入输出

echo

printf

printf "%s is %d years old\n" "bird" 18       #与c语言相似

read

在这里插入图片描述

用到的比较多的是 -t :限制输入的时间 -p:输入显示提示 -s:静默模式,输密码时,不显示出来

read -p "Please input your rassword:"
read -s -p "Please input your rassword:"
read -s -t 1 -p "Please input your rassword:"

函数

三种定义方式,记住第三种

fuction _printf_ {
	echo $1
	return
}

_printf_() {
	echo $1
	return
}

function _printf_() {
	echo $1
	return
}

调用:
_printf_  "Hello World!"

流程控制

IF

age=$1
#test表达式  -eq  -ne  -gt  -lt  -ge  -le

if [[ $age -gt 18 ]]; then
	echo "> 18"
	else
		echo "< 18"
fi


if [[ $age -gt 18 ]]; then
	echo "> 18"
elif [[ $age -le 18 ]]; then
		echo "< 18"
fi

WHILE

#!/bin/bash
#从100打印到0
num=100
while [[ ${num} -ge 0 ]];do
	echo &{num}
	num=$[${num} - 1]
done

FOR

for (( i = 0; i <= 100; i++ ));do
	echo ${i}
done

for i in `seq 0 100`;do
	echo $i
done

UNTIL

直到until后面的条件成立时,就结束

#!/bin/bash
#输出0~100
sum=0
until [[ ${sum} -gt 100  ]];do
    echo ${sum}
    sum=$[${sum}+1]
done

CASE

read age
case $age in
	1 )
		echo "1"
		;;
	2 ) 
		echo "2"
		;;
esac

数组与数组操作

bash 支持一维数组(不支持多维数组),并且没有限定数组的大小。

类似与 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

定义数组

array_name=(value0 value1 value2 value3)

array_name=(
value0
value1
value2
value3
)

还可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
可以不使用连续的下标,而且下标的范围没有限制。

读取数组

valuen=${array_name[2]}

#使用@ 或 * 可以获取数组中的所有元素,例如:
${array_name[*]}
${array_name[@]}


#找到数组下标
${!array[@]}  #数组中各项对应的下标


# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

数组追加

array+=(a b c)
#例子
name[0]=a
name[1]=b
name+=(1 2 3)
echo ${name[*]}
#输出   a b 1 2 3

数组排序

用管道 sort 排一下就可以

删除数组与元素

unset name[100]  #清空name[100]这个位置

实践 1

求一定范围内的素数和

采用线性筛

传入两个参数,起始数字,终止数字

如果起始数字<0, 则从0开始

使用方式 : bash Prime.sh 0 1000

#!/bin/bash
declare -a prime   #这里定义了一个数组prime,不定义直接用也没有关系
sum=0

function usage() {
	printf "Usage: %s start_num  end_num\n" $0
}

function init() {
	S=$1
	E=$2
	for (( i=$S; i<=$E; i++ ));do
		prime[$i]=0
	done
}

if [[ $# -ne 2 ]];then
	usage
	exit
fi

start_num=$1
end_num=$2

if [[ $start_num -lt 0 ]];then
	start_num=0
fi
if [[ ${start_num} -gt ${end_num} ]];then
	Usage 
	exit
fi

init 0 ${end_num}

for (( i=2; i<=${end_num}; i++));do
	if [[ ${prime[$i]} -eq 0 ]];then
		prime[0]=$[ ${prime[0]} + 1 ]
		prime[${prime[0]}]=$i
		if [[ $i -ge ${start_num} ]];then
			sum=$[ ${sum} + $i ]
		fi
	fi
	for (( j=1; j<=${prime[0]}; j++ ));do
		if [[ ${i} * ${prime[$j]} -gt ${end_num} ]];then
			break
		fi
		prime[$[ ${i} * ${prime[$j]} ]]=1
		if [[ $[ $i % ${prime[$j]} ] -eq 0 ]];then
			break
		fi
	done
done

echo $sum

实践 2

求一个文件中的最长字符串 , 用命令实现,非脚本

先要拿到文件中的所有字符串

使用tr指令

tr [-cdst][--help][--version][第一字符集][第二字符集] 
-c, --complement:反选设定字符。也就是符合 SET1 的部份不做处理,不符合的剩余部份才进行转换
-s, --squeeze-repeats:缩减连续重复的字符成指定的单个字符
cat a.cpp | tr -s -c "a-zA-Z" " "
-c:  反选,不是“a-zA-Z”的一律改为“ ”
-s:  压缩  不是“a-zA-Z”的多个字符,一律压缩成一个字符
#!/bin/bash
max_len=0
max_string=''
if [[ $# -lt 1 ]];then
	printf "Usage: %s file[...]\n" $0
	exit
fi

for i in `cat $1 | tr -s -c "a-zA-Z" "\n"`;do
	len=${#i}
	if [[ ${len} -gt ${max_len} ]];then
		max_len=$len
		mex_string=$i
	fi
done

echo $max_string $max_len

得到结果

function   8

#之后想要的到所在行号:
grep -n "function" a   #在a文件中找到"function"

实践 3

递归查找文件中的最长字符串

小插曲:如何读取stdio.h文件的行数

 wc -l `locate stdio.h | head -n 1`
 
 locate stdio.h 拿到许多路径  
 head -n 1   拿到许多路径中的第一个
 wc -l       统计文件中的行数

正题:

分成两个文件共同完成这个功能

  1. func.sh
#!/bin/bash

function find_max_in_file() {
	echo "finding max_string in file $1"
	words=`cat $1 | tr -s -c "a-zA-Z" "\n"`
    for i in ${words};do
    	len_t=`echo -n ${i} | wc -c`
    	if [[ ${len_t} -gt ${len_max} ]];then
    		len_max=${len_t}
    		max_string=${i}
    		max_file=$1
    	fi
    done
}

function find_max_in_dir() {
	for i in `ls -A $1`;do
		if [[ -d ${1}/${i} ]];then
			echo "${1}/$i is a dir"
			find_max_in_dir ${1}/${i}
		else
			echo "${1}/$i is a file"
			find_max_in_file ${1}/${i}
		fi
	done
}

注1:这里注意在dir中遍历其子文件时,要把"." 和"…"去掉,不然会无休止的递归

去掉的方法, ls -A 不同于ls -a, 没有".“和”…"

注2:以上还有一个问题,需要对文件种类进行过滤,两种过滤方法:

一个是用test表达式去判断是不是普通文件

一个是用文件名后缀来过滤

方法一:在原第4行和第5行之间添加test表达式判断

#!/bin/bash

function find_max_in_file() {
	echo "finding max_string in file $1"
	
	# 用test表达式来判断文件是不是regular文件
	if [[ ! -f ${1} ]];then
		echo "$1 is not a r_file!\n"
		return
	fi
	
	words=`cat $1 | tr -s -c "a-zA-Z" "\n"`
    for i in ${words};do
    	len_t=`echo -n ${i} | wc -c`
    	if [[ ${len_t} -gt ${len_max} ]];then
    		len_max=${len_t}
    		max_string=${i}
    		max_file=$1
    	fi
    done
}

function find_max_in_dir() {
	for i in `ls -A $1`;do
		if [[ -d ${1}/${i} ]];then
			echo "${1}/$i is a dir"
			find_max_in_dir ${1}/${i}
		else
			echo "${1}/$i is a file"
			find_max_in_file ${1}/${i}
		fi
	doneif [[ ! -f ${1} ]]
}

方法二: 添加一个黑名单

#!/bin/bash

filter_types=(mp4 avi gz zip tar)

function filter(){
	file_name=$1
	type_name=`basename ${file_name} | rev | cut -d "." -f 1 | rev`
	for i in ${filter_types[@]};do
		if [[ ${file_name} == $i ]];then
			echo "Filter On! ${file_name}"
			return 1   #这里返回1
		fi
	done
}

function find_max_in_file() {

	if [[ ! -f ${1} ]];then
		echo "$1 is not a r_file!\n"
		return
	fi
	#这里用bilter函数判断一下是否为黑名单中的后缀文件
	filter $1
	if [[ $? -eq 1 ]];then  #查看filter函数是否正常退出, 如果返回值是1,则直接返回
		return 
	fi
	
	echo "finding max_string in file $1"
	words=`cat $1 | tr -s -c "a-zA-Z" "\n"`
    for i in ${words};do
    	len_t=`echo -n ${i} | wc -c`
    	if [[ ${len_t} -gt ${len_max} ]];then
    		len_max=${len_t}
    		max_string=${i}
    		max_file=$1
    	fi
    done
}

function find_max_in_dir() {
	for i in `ls -A $1`;do
		if [[ -d ${1}/${i} ]];then
			echo "${1}/$i is a dir"
			find_max_in_dir ${1}/${i}
		else
			echo "${1}/$i is a file"
			find_max_in_file ${1}/${i}
		fi
	done
}
  1. find.sh
#!/bin/bash

len_max=0
max_string=""
max_file=""

source find_max_in_dir.sh

if [[ $# -eq 0 ]];then
    find_max_in_dir "."
else
    for i in $@;do
        if [[ -d $i ]];then
            find_max_in_dir $i
        else
            find_max_in_file $i
        fi
    done
fi

#彩色打印结果
printf "The max string is \033[31;34m%s \033[0m , with length \033[31;34m %d  \033[0m  in file \033[31;34m %s\033[0m\n" $max_string $len_max $max_file
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值