Linux Shell 使用手记

1.背景

Linux Shell是一种基本功,因为怪异的语法加之较差的可读性,通常被python等脚本取代。

既然是基本功,那就须要掌握。毕竟学习Shell脚本的过程中,还是能了解到非常多Linux系统的内容。Linux脚本大师不是人人都能够达到的。可是用一些简单的Shell实现一些常见的基本功能还是非常有必要的。

2.正题

1) 热身

以下的样例展示了怎样向脚本传递參数、脚本怎样获取參数、if-else推断、变量的使用等基本内容。

#!/bin/bash

if [[ $# -lt 1 ]]; then
        echo "args count must > 1"
        echo "Uage: bash +x example01.sh [args...]"
        exit
fi

arg=$1

if [[ $arg -gt 10 ]]; then
        echo "$arg > 10"
else
        echo "$arg < 10"
fi
这个脚本的调用方式例如以下:

bash +x example01.sh 5
2).数组、函数传參数,循环

以下的样例展示了数组、函数、循环等基本使用。

#!/bin/bash

if [[ $# -lt 1 ]]; then
	echo "args count must > 1"
	echo "Uage: bash +x example01.sh [args...]"
	exit
fi

args=$@

for arg in $args; do
	echo $arg
done

function fun() {
	echo $1
}

fun "hello shell"

fun2() {
	echo "Linux"
}

fun2
注意,函数fun中的$1。获取的是函数參数,不是脚本调用时传入的參数。

$@ 是获取脚本调用时传入的參数列表。

3).while 循环以及其它几种循环、case、表达式expr的使用

#!/bin/bash

if [[ $# -lt 1 ]]; then
        echo "args count must > 1"
        echo "Uage: bash +x example01.sh [args...]"
        exit
fi

case $1 in
        "install" )
                echo "operation type is install"
        ;;
        "uninstall" )
                echo "operation type is uninstall"
        ;;
        * )
                echo "operation type is not support"
        ;;
esac

for ((i=0;i<3;i++))
do
        if ((i==1))
        then
                continue
        fi
        echo $i
done

for i in `seq 5`
do
        echo "loop $i"
done


注意这里的case * 并非全部,而是输入值不在case中。相当于default. 在循环中能够使用continue/break等keyword,非常相似java等其它语言的循环。

4).脚本之间互相引用

通过source 或者 . 的方式能够引用另一个脚本中的函数或者变量

first.sh

function fun(){
	echo "i am from first."
}

file=first
second.sh

. first.sh
fun

echo $file

使用bash +x second.sh运行,在second.sh 中能够调用fun函数和使用file变量。

这里的.和source都能够实现引用first文件里的变量。注意: 假设同一时候引用了多个脚本的同一个变量名的变量。后面的值会覆盖前面的变量而不会报错。

5).关于错误处理

a)在shell中有一个变量 $?

。这个变量记录的是上次脚本运行的结果。假设正常结束则是0,否则是非0值。

b)假设在shell脚本中通过set -o errexit来实现遇到错误就退出,这样能够避免产生很多其它的错误;

c)在shell运行过程中假设出错,能够通过重定向的方式,输出到文件里,比方Command >> filename2>&1

6).字典

shell中的字典是非常好的数据结构,能够非常方便地处理配置

#!/bin/bash

set -o errexit

hput(){
	eval "hkey_$1"="$2"
}

hget(){
	eval echo '${'"hkey_$1"'}'
}

hput k1 v1
hget k1

declare -A dic

dic=([key1]="value1" [key2]="value2" [key3]="value3")

echo ${dic["key1"]}
# output all key
echo ${!dic[*]}
#outpull all value
echo ${dic[*]}
# access all
for key in $(echo ${!dic[*]})
do
        echo "$key : ${dic[$key]}"
done
运行之后,输出例如以下:

v1
value1
key3 key2 key1
value3 value2 value1
key3 : value3
key2 : value2
key1 : value1
7).文本处理

sed 命令能够对对文本进行操作。

比方有一个文件sedfile,内容例如以下:

1
2
3
4
5
运行 "sed '1,3d' sedfile,则会输出4。5 两行,即对1,2,3行做了删除处理。注意这时文件里面并没有删掉这两行

除了删除之外,还能够做替换操作。

root@ubuntu:~/codelab# sed 's/1/0/g' sedfile 
0
2
3
4
5
root@ubuntu:~/codelab# cat sedfile 
1
2
3
4
5
我们发现,在输出时。将1替换成了0;

文本处理另一个非常强大的工具-awk

我们首先看看awk的基本处理-依照逗号(,)切割获取想要的文本。

root@ubuntu:~/codelab# cat awkfile 
this,is,a,awk,file
this,is,a,awk,file
root@ubuntu:~/codelab# cat awkfile | awk -F ',' '{print $0}'
this,is,a,awk,file
this,is,a,awk,file
root@ubuntu:~/codelab# cat awkfile | awk -F ',' '{print $1}'
this
this
root@ubuntu:~/codelab# 
这里我们能够看到awk能够依照随意形式切割文本。然后输出;

8).特殊变量和值

下列变量通常具有固定意义

$#    表示变量的个数。经常使用于循环
$@    当前命令行全部參数。

置于双引號中,表示个别參数 $* 当前命令行全部參数。置于双引號中。表示将命令行全部參数当初一个单独參数 $-(连字号) 在引用数给予Shell的选项 $?

表示上一个命令退出的状态 $$ 表示当前进程编号 $0 表示当前程序名称 $! 表示近期一个后台命令的进程编号 $HOME 表示当前用户根文件夹 $IFS 表示内部的字段分隔符 $LANG 当前locale默认名称 $PATH 环境变量 $PPID 父进程编号 $PWD 当前工作文件夹

下列特殊值能够帮助排查问题

0    成功退出
>0    退出失败
1-125    命令退出失败,失败返回的相关值由程序定义(譬如,程序内退出仅仅运行 exit 2。则返回为2)
126    命令找到了,但无法运行
127    命令找不到
>128    命令因受到信号而死亡
9.定时任务

windows系统下有定时任务。在linux系统上也一样有定时任务。使用系统自带的crontab就可以实现;

我的系统上运行 cat /etc/crontab的输出例如以下:

root@ubuntu:~/codelab# cat /etc/crontab 
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
从这里我们能够看出cron是怎样使用的,这里设定了cron使用shell,以下是命令的设置方式。

minute: 表示分钟,能够是从0到59之间的不论什么整数。
hour:表示小时,能够是从0到23之间的不论什么整数。

day:表示日期。能够是从1到31之间的不论什么整数。 month:表示月份。能够是从1到12之间的不论什么整数。

week:表示星期几,能够是从0到7之间的不论什么整数,这里的0或7代表星期日。 command:要运行的命令,能够是系统命令,也能够是自己编写的脚本文件。

我们能够使用例如以下一些基础命令;

usage:	crontab [-u user] file
	crontab [ -u user ] [ -i ] { -e | -l | -r }
		(default operation is replace, per 1003.2)
	-e	(edit user's crontab)
	-l	(list user's crontab)
	-r	(delete user's crontab)
	-i	(prompt before deleting user's crontab)
10).技巧

a)推断參数是否是数字。

#!/bin/sh
#1. $1是脚本的第一个參数,这里作为awk命令的第一个參数传入给awk命令。
#2. 因为没有输入文件作为输入流。因此这里仅仅是在BEGIN块中完毕。

#3. 在awk中ARGV数组表示awk命令的參数数组,ARGV[0]表示命令本身,ARGV[1]表示第一个參数。

#4. match是awk的内置函数,返回值为匹配的正則表達式在字符串中(ARGV[1])的起始位置。没有找到返回0。 #5. 正則表達式的写法已经保证了匹配的字符串一定是十进制的正整数,如须要浮点数或负数,仅需改动正则就可以。 #6. awk运行完毕后将结果返回给isdigit变量,并作为其初始化值。

#7. isdigit=`echo $1 | awk '{ if (match($1, "^[0-9]+$") != 0) print "true"; else print "false" }' ` #8. 上面的写法也能实现该功能,可是因为有多个进程參与,因此效率低于以下的写法。 isdigit=`awk 'BEGIN { if (match(ARGV[1],"^[0-9]+$") != 0) print "true"; else print "false" }' $1` if [[ $isdigit == "true" ]]; then echo "This is numeric variable." number=$1

fi
b) 依据系统给变量赋值,确保可移植性
#1. 通过uname命令获取当前的系统名称。之后再依据OS名称的不同,给PING变量赋值不同的ping命令的全称。

osname=`uname -s` #2. 能够在case的条件中加入很多其它的操作系统名称。 case $osname in "Linux") PING=/usr/sbin/ping ;; "FreeBSD") PING=/sbin/ping ;; "SunOS") PING=/usr/sbin/ping ;; *) ;; esac

3.总结

这些仅仅是shell开发中的最基础部分。shell 事实上是一种很高效、强大的语言,不断学习和积累一定会越来越熟练。









posted on 2017-08-20 13:49  ljbguanli  阅读(232)  评论(0编辑  收藏  举报