SHELL学习笔记1

来源:互联网 发布:淘宝 手机 假货 编辑:程序博客网 时间:2024/05/01 20:01

1.清空日志

echo " " >/var/log/messages
>/var/log/messages
>cat /dev/null >test.txt

范例1:
#!/bin/bash
cd /var/log
cat  /dev/null > messages
echo "Logs cleaned up"

范例2:
#!/bin/bash
ROOT_UID=0
LOG_DIR=/var/log
if [ "$UID" -ne "ROOT_UID" ]
then
       echo "Must be root to run this script."
       exit 1
fi
cd $LOG_DIR || {echo "Can't change to necessary directory." >&2
exit 1
}
cat /dev/null > messages
echo "Logs cleaned up."
exit 0  #退出之前返回0表示成功;返回1表示失败。

# -eq:等于
# -ne:不等于
# -le:小于等于
# -ge:大于等于
# -lt:小于
# -gt:大于


Shell脚本与PHP,Perl,python语言的差别?
shell的优势在于处理操作系统底层的业务(大量的命令作为支撑,2000多个命令都是shell支撑)

2.Shell基础

"#!"又成为幻数,在执行bash脚本的时候,内核会根据它来确定哪个程序来解释脚本中的内容。这一行必须在脚本的第一行,如果不是第一行则为注释。
sh与bash的区别,我们发现sh本质为bash的软连接:xrwxrwx. 1 root root 4 May  8 10:07 /bin/sh -> bash


3.Shell脚本的执行

当shell脚本以非交互的方式运行时,它会先检查环境变量ENV,该变量支出一个环境文件(通常是.bashrc),然后从该变量环境变量文件开始执行,当读取了ENV文件后,Shell才开始执行shell脚本中的内容。
Shell脚本的执行有3种方式:
①bash script-name 或 sh script-name //当脚本本身没有x权限,或者脚本开头没有指定解释器。
②path/script-name 或 ./script-name
③source script-name 或 . script-name
这里注意第三种执行与前两个执行不同,第三种通常是使用source或者“.”来读入或加载指定的shell脚本,然后依次执行指定shell脚本文件san.sh中的所有语句。这些语句将作为父shell脚本father.sh进程中的一部分运行。因此,使用source或者“.”可以将san.sh脚本中的变量的值或函数等返回的值传递到当前父shell脚本father.sh中使用。
我们看接下来这个例子:
echo 'userdir=`pwd` >test.sh'
cat test.sh
userdir=`pwd`
sh test.sh
echo $userdir  //userdir变量并没有被传递到当前窗口shell中来。
我们发现没有任何结果,但是此时是空的。
然后我们用. test.sh运行一遍脚本,echo $userdir -> /uestc 此时有结果了。
这是为什么呢?
我们当前执行脚本的窗口是一个shell,而test.sh又处于另外一个shell中。因此当我们执行sh test.sh后,虽然test.sh中已经定义了userdir,但是无法将其传递到其父shell(当前窗口所处的shell)中来,而. /test.sh执行的方式就可以。

4.变量

可分为环境变量(全局变量)和局部变量
我们设置环境变量在用户家目录的.bash_profile文件中,或者/etc/bashrc,或者/etc/profile.d/,或者/etc/profile
环境变量可以在创建他们的shell和从该shell派生的任意子shell或进程中。他们通常被称为全局变量以区别局部变量。通常,环境变量应该大写。环境变量是用export内置命令导出的变量(或者export 变量名=value),可以是用declare -x导出。取消环境变量用unset 变量名。
本地变量是在用户当前的shell生存期的脚本中使用:定义方式为:locate UESTC
定义变量单引号、双引号与不加引号
不加引号:内容一般为简单连续的数字、字符串、路径名等。
单引号:输出变量时引号里面是什么就输出什么。
双引号:输出百年来那个时引号里的变量会经过解析后输出该变量内容。
注意在awk中有些不同,我们看下面的一个例子:
单引号:ETT=123
awk 'BEGIN {print '$ETT'}'
123
双引号:
awk 'BEGIN {print "$ETT"}'
$ETT
把命令定义为变量:
cmd=`date +%F`或者cmd=$(date +%F)
当我们对文件进行打包备份时为了显示时间可以这样搞:
tar -zcvf etc_$(date +%F)_backup.tar.gz /etc

5.Shell特殊变量

位置变量

$0 获得当前执行shell脚本的文件名,包含完整路径;只取名字:basename $0;只去路径:dirname $0
$n 获得当前指定行的shell脚本的第n个参数值,n=1,2,3...,9,当n大于等于10时就要用大括号括起来$(10);
$* 获得当前shell的所有参数,并将所有的命令行参数视为单个字符串;
$# 获得当前shell命令行中参数的总个数;
$@ 获得当前shell的所有参数,每个参数还是独立的。
实例:
# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status portmap
        ;;
  restart|reload)
        restart
        ;;
  condrestart)
        [ -f /var/lock/subsys/portmap ] && restart || :
        ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
        exit 1
esac

我们这里用实例比较一下$*和$@的区别
-rw-r--r--. 1 root root  330 May 25 08:42 user.log
[root@localhost uestc]# set -- "I am" handsome oldboy.
[root@localhost uestc]# echo $#
3
我们现在讲这三个参数全都打印出来。
[root@localhost uestc]# for i in $*; do echo $i; done //注意$*此时无引号
I
am
handsome
oldboy.

[root@localhost uestc]# for i in $@; do echo $i; done //注意$@此时输出结果无引号与$*一样
I
am
handsome
oldboy.

当两者都要引号的时候:
[root@localhost uestc]# for i in "$*"; do echo $i; done
I am handsome oldboy.
[root@localhost uestc]# for i in "$@"; do echo $i; done
I am
handsome
oldboy.
大家注意到他们的区别了吗?

进程状态变量

$$ 获取当前shell的进程号
$! 获得Shell最后运行后台的PID
$? 获得执行上一个指令的返回值(0为成功,非零为失败)
$_ 在此之前执行的命令或脚本的最后一个参数
这里我们举个$?的例子:
#!/bin/bash
cd /etc
tar -zcvf service.tar.gz ./services $>/dev/null
[ $? -eq 0 ] && echo OK || echo Failed
这里总结一下$?返回值的的意义:
0 成功
2 命令权限不够;
1~125 表示运行失败,脚本命令、系统命令错误或参数传递错误;
126 找到该命令,但是无法执行;
127 未找到要运行的命令;
大于128 命令被系统强制结束。
我们再来通过实例来比较一下$*和$@的不同
set -- "I am" an UESTC student          #传入四个参数
echo $#                                                  #目前有4个参数
4
for i in $*; do echo $i; done                 #循环打印这些参数
I
am
an UESTC
student
这个跟:for i in $@; do echo $i ; done 的作用相同
如果$*与$@都加上引号则会出现上面的不同。

另一个例子:
#!/bin/bash
echo $$ >/root/a.log
while true
do
    uptime >/dev/null 2>&1
    sleep 2
done

[root@localhost uestc]# sh ./2.sh &
[1] 4403
[root@localhost uestc]# cat /root/a.log 
4403

6.Bash内部命令变量及shift实践讲解

有些内部命令在目录列表是看不到的,可以通过man bash来查看。常用的有:echo、eval、exec,exportread,shift,exit和点(.)
shift语句按如下方式重新命令所有的位置参数变量,即$2成为$1,$3成为$2。在程序中每使用以此shift语句,都是所有的位置参数依次向左移动一个位置并使位置参数$#减1,直到减到0为止。
某些脚本加了一些选项再接参数,里面可能有shift n(n=1,2,...)进行切换,然后取$1就能选到所加的选项。
比如在/usr/bin/ssh-copy-id这个脚本中就有这些参数。 
eval(evalargs)读入参数args,并将它们组合成一个新的命令,然后执行。这后面日志中会涉及到。
exec 当Shell执行到exec语句,不会去创建新的子进程,而是转去执行指定的命令,当指定的命令执行完时,该进程(也就是最初的Shell)就终止了,所以Shell程序中exec后面的语句将不再被执行。

7.Shell变量的子串常用操作

${#string}                                               #返回$string的长度      //echo ${string} | wc -m也可以,但是这种计数比#string计数多一个
${string:position}                                  #从位置$position之后开始提取字串      //也就是从position+1的位置开始取
${string:position:length}                     #从位置$position之后开始提取长度为$length的字串
${string#substring}                              #从变量$string开头开始删除最短匹配$substring字串         //cut -c 2-4同样可以达到这种效果。
${string##substring}                            #从变量$string开头开始删除最长匹配$substring字串
${string%substring}                             #从变量$string结尾开始删除最短匹配$substring字串
${string%%substring}                         #从变量$string结尾开始删除最长匹配$substring字串
${string/substring/replace}                 #使用$replace,来代替第一个匹配的$substring
${string/#substring/replace}              #如果$string前缀匹配$substring,就用$replace来代替匹配$substring
${string/%substring/replace}             #如果$string后缀匹配$substring,就用$replace来代替匹配$substring
这里我们举一些实际中的例子来解释在实际生产中的实践:批量文件该名字
[root@localhost test]# ls
stu_102999_1_finished.jpg
stu_102999_2_finished.jpg
stu_102999_3_finished.jpg
stu_102999_4_finished.jpg
首先我们如何创建这些文件呢?
先把这些文件名字复制到a.log文件中去,然后我进入到test目录内
for f in `cat a.log` ; do touch $f ; done
我们需要将每个文件后面的finished字串去掉,该如何去做呢?
这里我们可以是用${string%substring}进行操作。
#!/bin/bash
for f in `ls *.jpg`
do
  mv $f `echo ${%fi
fnished*}.jpg`
done
另外我们也可以用上替换命令${string/%substring/replace}该文件扩展名。
比如我们把上面文件扩展名.jpe改为大写的.JPG
#!/bin/bash
for f in `ls *.jpg`
do
  mv $f `echo ${f/%jpg/JPG}`
done
或者通过sed命令
for f in `ls *.jpg`
do
   mv $f `echo $f | sed 's/jpg/JPG/g'`
done
这种方法同样可以
最简单的方法是通过rename命令来实施
rename "finished" '' *
同样改扩展名:rename .jpg .JPG *
rename的具体用法如下图:


扩展:其他变量的
${value:-word} 如果变量名存在且非空,则返回变量的值。否则,返回word字符串用途:如果变量未定义,则返回默认值。范例:${value:-word},如果value未定义,则表达式的值为word。
${value:=word} 如果变量名非空,则返回变量值。否则,设置这个变量值为word,并返回其值。用途:如果变量未定义,则设置变量为默认值,并返回默认值。范例:${value:=word},如果value为定义,则设置value值为word,返回表达式的值也为word。
${value:?message} 如果变量赋值的话正常替换,否则将消息message送到标准错误输出()
实例:
path1="/uestc"
rm -rf $path1
这里看似没什么问题,但是假如变量不存在或者设置错误,可能导致系统从根目录开始删起。吓!!!
我们可以通过上面的讲的进行优化:
path1="/uestc"
rm -rf ${path:-/tmp/}
这样之后就比较保险了,及时目录不存在就会删tmp目录内的临时文件,这样就很安全了。
在执行脚本之前,我们可以是用sh -x来对脚本进行调试。
生产场景可以看这两个系统脚本:
/etc/init.d/httpd
/etc/init.d/crond
我们再学习脚本的时候可以多看一些系统脚本文件,可以给系统脚本写目录。
0 0
原创粉丝点击