标签归档:Shell

Bash Shell 变量

在Bash当中,当一个变量名称未被设置时,默认的内容是空的。

变量的设置规则:
1) 变量与变量内容以一个等号连接,等号两边不能接空格,注意,如果有空格则是逻辑比较
2) 变量名称只能是英文字母和数字,但开头不能是数字
3) 变量内容如含有空格,需要使用单引号或双引号括起来(如没有引号括起来,但是包含了转义,最终会转义输出),但双引号内特殊字符保持本性,单引号内的任何字符都是一般文本
4) 可以使用转义符将特殊字符变成一般字符
5) 如需要引用其它命令的结果,可以使用反单引号或$()语法,比如`uname –r`和$(uname -r)等同
6) 如要为变量增加内容,可以使用$变量名或${变量名}累加内容(不需要所谓的连接符)
7) 如变量需要在其它子进程中执行,需要以export来使变量变成环境变量:export PATH。
8) 通常大写字符为系统默认变量
9) 取消变量的方法是使用unset 变量名称

环境变量与自定义变量
在一个Shell中启用一个子Shell,子Shell中会继承父Shell的环境变量,但是自定义变量不会被继承(除非自定义的变量声明为环境变量)。
查看当前Shell中的环境变量,可以使用env命令。
查看当前Shell中的所有变量(包括环境变量),可以使用set命令。
命令set可以查看已经存在的Shell变量,以及设置Shell变量的新变量值(可改环境变量值),但是不能定义新的Shell变量。
命令declare用来定义自定义变量,如果需要把自定义变量变成环境变量,需要使用export变量名;如果需要直接声明一个环境变量,可以使用declare +x格式。
如果需要直接设置一个环境变量,可以使用export var=value格式。

自定义变量是指用户自己定义的变量,定义时可以声明它为环境变量(+x),也可以之后重新以declare +x格式变换成环境变量,或以export方式将其变为环境变量。
自定义变量不会因为它变成了环境变量而脱离它是自定义变量的事实。
变量变成环境变量后,可以set一个新值,可以unset,但是无法去掉环境变量标签(即:总是环境变量,也总是自定义变量)。

#查看环境变量
env

#查看环境变量和自定义变量
set

#查看自定义变量(不管定义是不是环境变量)
#export var=value,也是自定义变量
declare

#修改变量值(var必须先存着)
set var=value

#定义一个环境变量
export var=value

#定义一个自定义变量
declare var=value

#把自定义变量转换为环境变量
export var

#定义一个环境变量(或把变量变成环境变量)
declare -x var

#取消变量
unset var

当export一个变量,实际应该调用的是declare +x,所以export默认的输出是自定义变量变成环境变量的列表:

export
declare -x USER="root"
declare -x XDG_RUNTIME_DIR="/run/user/0"
declare -x XDG_SESSION_ID="2237"
declare -x var="value"

总之,用户自定义的变量,永远是自定义的,这个自定义的变量可以变换为环境变量(但是还是自定义的)。env查看环境变量,declare查看自定义变量(包括自定义变成环境变量的变量),set查看所有变量(环境变量和自定义变量的并集),export可以查看有哪些自定义变量升级为环境变量。 可以使用declare定义变量(+x声明为环境变量),export可以把变量转换为环境变量(实际是declare +x的使用),set对存在的变量设置新的值。一般使用declare定义一个自定义变量(或定义时指定值),export定义一个环境变量(或定义时指定值,export是declare的一种用法),用set来改变变量值。

如果一个变量同时出现在env和declare的输出结果中,说明这个变量是从自定义变量升级为环境变量的。如果只出现在env结果中,说明不是由自定义变量升级而来。

如下图:

特别用法:

#若指令返回值不等于0,则立即退出Shell
set -e

Bash Shell脚本实例

–IP黑名单:

#!/bin/bash

iptables=`which iptables`
grep=`which grep`
backlist=./backlist
whitelist=./whitelist
url=http://x.x.x.x/api/ip/back-list

#跟新黑名单
code=`curl -I -m 10 -o /dev/null -s -w %{http_code} $url`

if [ "200" == "$code" ]; then 
     content=`curl -m 30 -s $url`
     if [ -n "$content" ]; then
         d=`echo $content | base64 -d > $backlist`
     fi
fi

#黑名单
if [ -f "$backlist" ]; then
while read line
do 
    ip=`echo $line | sed 's/[^0-9\.]*//g'`
    if [ -n "$ip" ]; then
        valid=$(echo $ip|awk -F. '$1<=255 && $1 > 0 && $2<=255 && $2>=0 && $3<=255 && $3>=0 && $4<=255 && $4>0 {print "1"}')
        if [ "$valid" == "1" ]; then
            s=`$iptables -L -n | $grep $ip`
            if [ -z "$s" ]; then
                r=`$iptables -A INPUT -s $ip -j DROP`
            fi
        fi
    fi
done < $backlist
fi

#白名单
if [ -f "$whitelist" ]; then
while read line
do 
    ip=`echo $line | sed 's/[^0-9\.]*//g'`
    if [ -n "$ip" ]; then
        r=`$iptables -D INPUT -s $ip -j DROP`
    fi
done < $whitelist
fi

根据IP获取到IP黑名单,跟新到backlist文件,然后读取文件,通过iptables封杀。读取whitelist,解除白名单封杀。

调用API获取黑名单时,通过curl -I -m 10 -o /dev/null -s -w %{http_code} $url,返回http状态码。

取回的内容是base64编码的,使用base64 -d进行解码还原为原始字符串保存到文件。

从文件读一行时,去掉除了数字和点的所有字符:sed ‘s/[^0-9\.]*//g’

读取到的IP如不为空,需要验证IP是合法的,使用:valid=$(echo $ip|awk -F. ‘$1<=255 && $1 > 0 && $2<=255 && $2>=0 && $3<=255 && $3>=0 && $4<=255 && $4>0 {print “1”}’)

–定时杀进程,并维持进程数量

#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#保持多少个进程
hold=2
if [ -n "$1" ]; then
    hold=$1
fi

#内存占用阈值(100M)
threshold=102400

#获取具体路径
pwd=$(cd `dirname $0`; pwd)

#搜索进程
search=$pwd'/artisan queue:work --queue=xx'

#循环杀掉超过阈值的进程
ps aux | grep $search | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`
    if [ $rss -gt $threshold ]; then
        kill=`$cmm -9 $pid`
        date=`date "+%Y-%m-%d %H:%M:%S"`
        echo $date' - 'PID:$pid' - RSS('$rss'K) > Threshold('$threshold'K) - Was Killed.'
    fi
done

#杀掉后维持进程数量
live=`ps -efH | grep $search | grep -v 'grep' | wc -l`
if [ $live -lt $hold ]; then
    for ((i=1; i<=$hold-$live; i=i+1))
    do
        php $search' --tries=3 --sleep=2 --timeout=300' 2>&1 >> /dev/null &
    done
fi

exit 0
[/shell]
进程内存超过阀值,杀掉重启。

取到脚本所在的路径:$(cd `dirname $0`; pwd)

获取到当前时间:date "+%Y-%m-%d %H:%M:%S"

命令ps aux的第二段和第六段是进程号和进程内存(RSS,单位KB)


--接收脚本参数杀进程并重启:

#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#操作系统位数
bit='32'
if [ $(getconf WORD_BIT) = '32' -a $(getconf LONG_BIT) = '64' ]; then
    bit='64'
fi

#端口
port=9515
if [ -n "$1" ]; then
    port=$1
fi

#获取具体路径
pwd=$(cd `dirname $0`; pwd)

#搜索进程
search=$pwd/chromedriver_linux$bit' --port='$port

#杀掉原来进程
ps aux | grep $search | grep -v grep | awk '{print $2}' | cat | while read pid
do
    kill=`$cmm -9 $pid`
    date=`date "+%Y-%m-%d %H:%M:%S"`
    echo $date' - 'PID:$pid' Was Killed.'
done

#重启进程
$pwd/chromedriver_linux$bit --port=$port 2>&1 >> /dev/null &

exit 0

##############################################################
##定时杀firefox

#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#循环杀掉进程
ps aux | grep 'firefox/firefox' | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`

    kill=`$cmm -9 $pid`
    date=`date "+%Y-%m-%d %H:%M:%S"`
    echo $date' - 'PID:$pid' - RSS('$rss'K) Threshold('$threshold'K) - Firefox Was Killed.'
done

exit 0

接收脚本参数:第一个$1,第二个$2, $0是脚本名称。 获取脚本的目录使用dirname

判断操作系统位数:$(getconf WORD_BIT) = ’32’ -a $(getconf LONG_BIT) = ’64’

–封杀某个区域的IP

#下载中国IP库
wget http://www.ipdeny.com/ipblocks/data/countries/cn.zone

#运行以下脚本
#!/bin/bash

COUNTRY = "cn"
IPTABLES = /sbin/iptables
EGREP = /bin/egrep

if [ "$(id -u)" != "0" ]; then
   echo "you must be root" 1>&2
   exit 1
fi

resetrules() {
    $IPTABLES -F
    $IPTABLES -t nat -F
    $IPTABLES -t mangle -F
    $IPTABLES -X
}

resetrules

for c in $COUNTRY
do
        country_file = $c.zone

        IPS = $($EGREP -v "^#|^$" $country_file)
        for ip in $IPS
        do
           echo "blocking $ip"
           $IPTABLES -A INPUT -s $ip -j DROP
        done
done

exit 0

–防DD脚本

#!/bin/sh

IGNORE_IP_LIST="/usr/local/ddos/ignore.ip.list"
IPT="/sbin/iptables"

KILL=1

# NO_OF_CONNECTIONS默认是150,这是一个经验值,如果服务器性能比较高,可以设置200以上,以避免误杀
NO_OF_CONNECTIONS=150

# 解锁的时间,单位为秒,可以设置更长时间
BAN_PERIOD=86400

# 解除对IP的封锁 过程就是运行一个睡眠脚本
unbanip()
{
	# 产生解除IP封锁时用到的随机脚本文件
        UNBAN_SCRIPT=`mktemp /tmp/unban.XXXXXXXX`

	# 临时文件
        TMP_FILE=`mktemp /tmp/unban.XXXXXXXX`

	# 将被解除封锁的IP
        UNBAN_IP_LIST=`mktemp /tmp/unban.XXXXXXXX`

	# 产生解除IP封锁的脚本内容
        echo '#!/bin/sh' > $UNBAN_SCRIPT
	# $BAN_PERIOD睡眠时间,表示$UNBAN_SCRIPT睡眠多久后继续
        echo "sleep $BAN_PERIOD" >> $UNBAN_SCRIPT

	# 输入重定向,行对于$line变量,是当前需要堵塞的IP,脚本运行过程中产生
	while read line; do
		# 解除IP封锁
		echo "$IPT -D INPUT -s $line -j DROP" >> $UNBAN_SCRIPT

		# 把将要解除堵塞的IP写入$UNBAN_IP_LIST
		echo $line >> $UNBAN_IP_LIST
	done < $BANNED_IP_LIST

        # 从$IGNORE_IP_LIST中去掉$UNBAN_IP_LIST,把结果写入$TMP_FILE(差集)
        echo "grep -v --file=$UNBAN_IP_LIST $IGNORE_IP_LIST > $TMP_FILE" >> $UNBAN_SCRIPT

	# 移动$TMP_FILE到$IGNORE_IP_LIST
        echo "mv $TMP_FILE $IGNORE_IP_LIST" >> $UNBAN_SCRIPT

	# 删除$UNBAN_SCRIPT
        echo "rm -f $UNBAN_SCRIPT" >> $UNBAN_SCRIPT
	# 删除$UNBAN_IP_LIST
        echo "rm -f $UNBAN_IP_LIST" >> $UNBAN_SCRIPT
	# 删除$TMP_FILE,经过上面的移动操作,$TMP_FILE其实已经不存在
        echo "rm -f $TMP_FILE" >> $UNBAN_SCRIPT

	# 在后台运行$UNBAN_SCRIPT
	. $UNBAN_SCRIPT &
}


TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"

# 产生临时文件,存放已经被堵塞的IP
BANNED_IP_LIST=`$TMP_FILE`

# 产生临时文件,存放当前可能被堵塞的IP
BAD_IP_LIST=`$TMP_FILE`

#取回IP列表并按照数量从高到底排序
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

# 如果配置为需要堵塞
if [ $KILL -eq 1 ]; then            
	# 超过100就记录
	NUM_CONNECTIONS=100  
	# 按照日期存放
	CNTS_LOG="/usr/local/ddos/$(date +%Y)/$(date +%m)/"
	mkdir -p $CNTS_LOG
	CNTS_LOG="$CNTS_LOG$(date +%Y%m%d).log"
	while read line; do
		CURR_CONN=$(echo $line | cut -d" " -f1)
		CURR_IP=$(echo $line | cut -d" " -f2)
		if [ $CURR_CONN -lt $NUM_CONNECTIONS ]; then
			break
		fi
		echo "$CURR_IP with $CURR_CONN connections at `date`" >> $CNTS_LOG
	done < $BAD_IP_LIST

        IP_BAN_NOW=0
        while read line; do
		# 当前这个IP有多少个连接
                CURR_LINE_CONN=$(echo $line | cut -d" " -f1)            
		# 当前IP
                CURR_LINE_IP=$(echo $line | cut -d" " -f2)          

		# 如果这个IP链接数小于预设,终止(因为数据经过排序)
                if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then     
			break
                fi

		# 计算当前IP在$IGNORE_IP_LIST出现了多少次
                IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
		# 如果当前IP已经在$IGNORE_IP_LIST,跳过,可以通过这个方式设置永不堵塞某些IP
                if [ $IGNORE_BAN -ge 1 ]; then
			continue
                fi

		# 进入到这里表示当前必定有IP要被堵塞
                IP_BAN_NOW=1

		# 添加堵塞了的IP到当前堵塞列表,$BANNED_IP_LIST会应用到unbanip函数
                # 如果不实现自动解锁,注释如下行
                echo $CURR_LINE_IP >> $BANNED_IP_LIST

		# 添加堵塞了的IP到$IGNORE_IP_LIST
                # 如果不实现自动解锁,注释如下行
                echo $CURR_LINE_IP >> $IGNORE_IP_LIST
                
		# 开始iptables封锁
                $IPT -I INPUT -s $CURR_LINE_IP -j DROP
                
        done < $BAD_IP_LIST

	# $IP_BAN_NOW等于1表示有IP被封锁了
        if [ $IP_BAN_NOW -eq 1 ]; then
		# 发送通知

		# 同时开始运行解除堵塞程序
                #unbanip                             
        fi
fi

# 清除临时产生的文件
rm -f $TMP_PREFIX.*

函数unbanip实际就是运行一个睡眠脚本,可以把其去掉(不自动解封):

#!/bin/sh

IGNORE_IP_LIST="/usr/local/ddos/ignore.ip.list"
IPT="/sbin/iptables"
KILL=1
NO_OF_CONNECTIONS=150

TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"


BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

if [ $KILL -eq 1 ]; then
        IP_BAN_NOW=0
        while read line; do
		# 当前这个IP有多少个连接
                CURR_LINE_CONN=$(echo $line | cut -d" " -f1)            
		# 当前IP
                CURR_LINE_IP=$(echo $line | cut -d" " -f2)          

		# 如果这个IP链接数小于预设,终止(因为数据经过排序)
                if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then     
			break
                fi

		# 计算当前IP在$IGNORE_IP_LIST出现了多少次
                IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
		# 如果当前IP已经在$IGNORE_IP_LIST,跳过,可以通过这个方式设置永不堵塞某些IP
                if [ $IGNORE_BAN -ge 1 ]; then
			continue
                fi

		# 进入到这里表示当前必定有IP要被堵塞
                IP_BAN_NOW=1
                
		# 开始iptables封锁
                $IPT -I INPUT -s $CURR_LINE_IP -j DROP
        done < $BAD_IP_LIST

	# $IP_BAN_NOW等于1表示有IP被封锁了
        if [ $IP_BAN_NOW -eq 1 ]; then
		# 发送通知 CURL                           
        fi
fi

# 清除临时产生的文件
rm -f $TMP_PREFIX.*

如果仅仅希望监控一下哪些IP链接数过多,则可以如下改造:

#!/bin/sh

CNTS_LOG="/root/dd/log.txt"
IGNORE_IP_LIST="/root/dd/ignore.ip.list"
CONNECTIONS=5
 
TMP_PREFIX='/tmp/dd'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"

BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

while read line; do
    CURR_CONN=$(echo $line | cut -d" " -f1)
    CURR_IP=$(echo $line | cut -d" " -f2)
    if [ $CURR_CONN -lt $CONNECTIONS ]; then
        break
    fi

    IGNORE=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
    if [ $IGNORE -ge 1 ]; then
        continue
    fi

    echo "`date "+%Y-%m-%d %H:%M:%S"`|$CURR_IP|$CURR_CONN" >> $CNTS_LOG
done < $BAD_IP_LIST
 
rm -f $TMP_PREFIX.*

–数据库备份:

#!/bin/bash

hold=10
if [ -n "$1" ]; then
    hold=$1
fi

date=`date -d "-$hold day" +"%Y-%m-%d"`.sql

pwd=$(cd `dirname $0`; pwd)

for file in $(ls $pwd/a)
do 
    if [[ $file < $date ]]; then
         rm -f $pwd/a/$file
    fi
done

for file in $(ls $pwd/b)
do
    if [[ $file < $date ]]; then
         rm -f $pwd/b/$file
    fi
done

today=`date +"%Y-%m-%d"`

mysqldump -uroot -pxxx a > $pwd/a/$today.sql

mysqldump -uroot -pxxx b > $pwd/b/$today.sql

exit 0

保留x天,每天清理过期的,备份当天的。 库不大时,这个做法还是非常简单有效的。

Linux Bash Shell编程

Bash Shell基础

1 Bash Shell

1.1 系统的合法shell与/etc/shells
Linux下可以使用的shell,可以检查一下/etc/shells这个文件,有下面几个可以用的shell:

#CentOS 7.X中的输出
cat /etc/shells

/bin/sh             #/bin/bash的链接
/bin/bash
/sbin/nologin       #用户不具备Shell环境(一般指无法登陆),指定这个值

/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin

/bin/tcsh
/bin/csh

系统某些服务在运行过程中,会去检查用户能够使用的Shell,而这些Shell的查询就是借助/etc/shells这个文件。在/etc/passwd这个文件内,每行的最后一个段指明了用户使用的shell(如果一个用户不能登录,通常是/sbin/nologin)。

1.2 Bash Shell功能
1) 命令记忆能力(history)
在用户的主文件夹内的.bash_history用来记录历史记录,但是它记录的是前一次登录以前所执行过得命令,而至于这一次登录所执行的命令都被暂存在临时内存中,当注销系统后,该命令记忆才会记录到.bash_history中。
2) 命令与文件补全功能
3) 命令别名设置功能(alias)
可以使用alias直接设置命令别名,直接输入alias可以输出当前存在的命令别名。

alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

4) 作业控制、前台、后台控制(job control, foreground, background)
5) 程序脚本
6) 通配符(Wildcard)

1.3 Bash Shell的内置命令: type
为了方便Shell的操作,Bash已经内置了很多命令,利用type可以查看命令是否是内置命令。

2 Shell的变量

2.1 变量的显示与设置: echo unset
在Bash当中,当一个变量名称未被设置时,默认的内容是空的。变量的设置规则:
1) 变量与变量内容以一个等号连接,等号两边不能接空格,注意,如果有空格则是逻辑比较
2) 变量名称只能是英文字母和数字,但开头不能是数字
3) 变量内容如含有空格,需要使用单引号或双引号括起来(如没有引号括起来,但是包含了转义,最终会转义输出),但双引号内特殊字符保持本性,单引号内的任何字符都是一般文本
4) 可以使用转义符将特殊字符变成一般字符
5) 如需要引用其它命令的结果,可以使用反单引号或$()语法,比如`uname –r`和$(uname -r)等同
6) 如要为变量增加内容,可以使用$变量名或${变量名}累加内容(不需要所谓的连接符)
7) 如变量需要在其它子进程中执行,需要以export来使变量变成环境变量:export PATH。
8) 通常大写字符为系统默认变量
9) 取消变量的方法是使用unset 变量名称

2.2 环境变量

用env查看环境变量:

env

XDG_SESSION_ID=167086
HOSTNAME=xxxxx  #主机名
TERM=xterm
SHELL=/bin/bash  #使用的是哪个Shell
HISTSIZE=1000  #历史记录大小
SSH_CLIENT=113.88.168.31 17444 22
SSH_TTY=/dev/pts/3
USER=root
LS_COLORS=
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin  #执行文件查找的路径
PWD=/root
LANG=en_US.UTF-8 #语系数据,中文编码通常是zh_CN.UTF-8
HISTCONTROL=ignoredups
SHLVL=1
HOME=/root  #代表用户主文件夹
LOGNAME=root
SSH_CONNECTION=113.88.168.31 17444 120.24.42.192 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env

用set查看所有变量(含环境变量与自定义变量)

PS1     提示符号设置,可以man bash查询PS1的相关说明
$       当前的Shell的PID,$也是一个变量,要引用时使用$$
?       上个命令的回传码,一般,一个命令成功执行,则会回传一个0值
OSTYPE, HOSTTYPE, MACHTYPE 主机硬件与内核的等级

自定义变量转成环境变量
子进程会继承父进程的环境变量,不会继承自定义变量。直接执行export 变量名,可以把变量转成环境变量,如果要相反操作,用declare。

2.3 影响显示结果的语系变量(locale)
可以使用locale来查询Linux支持多少语系。locale –a其实是列出/usr/lib/locale里面的文件。当设置了LANG或者是LC_ALL时,则其他的语系变量就会被这两个变量所替代。LC_ALL是整体的语系变量,它在/etc/sysconfig/i18n中设置:LANG=”zh_CN.UTF-8”。(CentOS 7.x中对应的配置文件是/etc/locale.conf文件)

2.4 变量键盘读取、数组与声明: read,array,declare
1) read

用法:
read [-pt] variable
参数:
-p 后面可以接提示符
-t 后面可以接等待秒数
 
例子:
read –p “Please putin your name:” –t 30 named
等待用户30秒输入它的名字赋值给named变量

2) declare / typeset
declare / typeset都用来声明变量的类型。

用法:
declare [-aixr] variable
参数:
-a 定义为数据
-i 定义为整数
-x 定义成环境变量
-r 定义变量为只读

默认Bash对于变量有几个基本定义:
变量类型默认为字符串,所以若不指定变量类型,则1+2为一个字符串而不是计算式
Bash环境中的数值运算,默认最多仅能到达整数,所以1/3结果是0

另,declare +x sum 把 – 换成 + 代表取消操作。一般,除非需要特别指定变量类型,否则都不需要使用declare关键字。

3) 数组变量类型
在Bash里,数组的设置方式:var[index]=content

2.5 与文件系统及程序的限制关系: ulimit
Bash是可以限制用户的某些系统资源的,包括可以打开的文件数量、可以使用的CPU时间、可以使用的内存总量等。

用法:
ulimit [-SHacdfltu] [配额]
-H hard limit
-S soft limit,可以超过,但有警告值,必须比hard小
-a 后面不接任何参数,可列出所有的限制额度
-f 此shell可以创建的最大文件容量
-d
-l 可用锁定的内存量
-t 可使用的最大CPU时间
-u 单用户可以使用最大进程数量

2.6 变量内容的删除、替代与替换

3 命令别名与历史命令

3.1 命令别名设置:alias, unalias
3.2 历史命令:history

4 Bash Shell的操作环境

4.1 路径与命令查找顺序
命令运行顺序:
1) 以相对/绝对路径执行命令,如 /bin/ls 或 ./ls (最优先)
2) 由alias找到该命令来执行
3) 由bash内置(bulitin)命令来执行
4) 通过$PATH变量的顺序找到第一个命令来执行

4.2 bash的登录与欢迎信息:/etc/issue, /etc/motd

#查看/etc/issue:
CeontOS release 5.6 (Final)
Kernel \r on an \m

#如同$PS1变量一样,issue这个文件的内容也可以使用反斜杠为变量调用:
\d  本地时间的日期
\l  显示第几个终端机接口
\m  显示硬件等级
\n  显示主机网络名称
\o  显示domain name
\r  操作系统的版本(相当于uname -r)
\t  显示本地端时间的时间
\s  操作系统名称
\v  操作系统版本

如果你想要让用户登录后取得一些信息,那么可以将信息加入/etc/motd里面,这样所有人登录后都会获取此信息。

4.3 Bash的环境配置文件

4.3.1 登录Shell 和 非登录Shell
登录Shell需要完整的登录流程,非登录Shell取得Bash接口的方法不需要重复登录的流程。

登录Shell 和 非登录Shell它们读取的配置文件数据并不一样。
登录Shell只会读取如下两个配置文件:
1) /etc/profile: 这是系统整体的设置,最好勿改动此文件
2) ~/.bash_profile 或 ~/.bash_login 或 ~/.profile: 属于用户个人设置,要改自己的数据就放这里。

4.3.2 /etc/profile
这个配置文件可以利用用户的标示符UID来决定很多重要的变量数据。每个用户登录取得Bash时一定会读取的配置文件。这个文件设置的变量主要有:PATH, MAIL, USER, HOSTNAME, HISTSIZE。 除此还调用外部数据:
1) /etc/inputrc
/etc/profile会主动判读用户有没有自定输入的按键功能,如果没有,它就会决定设置”INPUTRC=/etc/inputrc”这个变量。此文件内容为Bash的热键等数据。
2 /etc/profile.d/*.sh
这个目录下面的文件规定了Bash操作接口的颜色、语系、ll与ls命令的命令别名、vi的命令别名、which的命令别名等。如果需要帮所有用户设置一些共享的命名别名时,可以在这个目录下面创建.sh的文件,并写入数据。

4.3.3 /etc/sysconfig/i18n
这个文件是由/etc/profile.d/lang.sh调用的。bash默认使用何种语系的配置文件。

4.3.4 ~/.bash_profile
~/.bash_profile 或 ~/.bash_login 或 ~/.profile从左到右,只会读取其中一个。查看~/.bash_profile发现,它其实还运行了. ~/.bashrc

在CentOS的登录Shell环境下,最终被读取的配置文件是”~/.bashrc”这个文件。所以可以将自己的偏好设置写入该文件。

4.3.5 source: 读入环境配置文件的命令

用法:
source 配置文件名
例子:source ~/.bashrc 和 . ~/.bashrc一样

利用source或小数点都可以将配置文件的内容读进当前的shell环境中。

4.3.6 ~/.bashrc
非登录Shell仅会读取~/.bashrc。查看其内容发现它会主动调用/etc/bashrc,而它定义如下数据:
依据不同的UID规定umask的值和提示符(PS1)
调用/etc/profile.d/*.sh的设置

4.3.7 其它相关配置文件
/etc/man.conf
规定了使用man的时候man page的路径到哪里去寻找。
~/.bash_history
~/.bash_logout
当注销bash后系统再所完什么操作后才离开。

4.4 终端机的环境设置:stty, set

4.5 通配符与特殊符号

*       0个或多个
?       任意一个字符
[]      在其中的一个字符
[-]     如[a-z] [0-9]
[^]     排除之中的字符的一个字任意字符

#除了通配符,bash中的特殊符号:
#       注释
\       转移符号
|       管道
;       分隔符号
~       用户的主文件夹
$       变量引用符
&       后台运行
!       逻辑运算非
/       目录符
>, >>   数据流重定向,输出导向

5 数据流重定向

执行一个命令的时候,这个命令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。标准输出与标准错误输出这两个命令默认都是输出到屏幕上面来。标准输出指的是命令执行所回传的正确的信息,而标准错误输出可理解为命令执行失败后,所回传的错误信息。

数据流重定向可以将stdout与stderr分别传送到其它的文件或设备去,所用到的特殊字符:
1) 标准输入(stdin): 代码为0,使用< 或 <<(指定结束输入的定界符); [bash] cat > catfile << "eof" [/bash] cat从标准输入接收输入然后显示,这里定位到catfile中,输入遇到定界符eof即刻结束输入。 2) 标准输出(stdout):代码为1,使用> 或 >>
3) 标准错误输出(stderr): 代码为2,使用2> 或 2>>

/dev/null垃圾桶设备与特殊写法
丢弃错误信息
2> /dev/null
如果所有信息都丢弃或都写入一个文件
> /dev/null 2>&1 或 &> /dev/null 此为特殊写法

标准输入: < 与<<
将原本需要有键盘输入的数据改由文件内容来替代。

命令执行的判断依据(; , && ||)
使用逗号可以在一行中连续执行多个命令
每个命令执行都有回传码,成功执行回传0,失败回传1,bash使用$?在动态记录这个这个值(?是一个变量,$也是变量,表示Bash的进程,使用$$来引用)。&& 和 ||可以用来分隔两个命令,使用&&时表示第一个命令执行成功则执行第二个,||表示第一个执行失败则执行第二个(判断依据是当前bash的$?的记录,所以当有多个&& 和 ||时需要注意,它判断的依据不是前面的命令,而是当前的bash记录的$?,当然,命令的执行会影响到$?值)

6 管道命令(pipe)

管道命令使用 | 界定符号。管道命令 | 仅能处理经由前面一个命令传来的正确信息,也就是标准输出的信息,对于标准错误输出并没有直接处理的能力。

在每个管道后面接的第一个数据必定是命令,而且这个命令必须要能接收标准输入的数据才行,这样的命令才可以是管道命令,管道命令有两个要注意的地方:
管道命令仅会处理标准输出,对于标准错误输出会予以忽略
管道命令必须要能够接收来自前一个命令的数据成为标准输入继续处理才行。

6.1 选取命令: cut, grep
一般,选取信息通常是针对行来分析的,并不是整篇信息分析的。

cut
主要用途在于将同一行里面的数据进行分解。

用法:
cut –d ‘分隔符’ -f fields
cut –c 字符范围
参数:
-d 后面接分隔字符,与-f 一起使用
-f 依据-d的分隔字符将一段信息切割为数段,用-f 取出第几段的意思
-c 以字符的单位取出固定字段区间
 
例子:
echo $PATH | cut -d ‘:’ -f 5 用:分割,取出第5段
echo $PATH | cut –d ‘:’ -f 3,5 用:分割,取出第3和第5段

cut在处理多空格相连的数据时比较困难。

grep
分析一行信息,如果符合条件则把整行信息输出。

用法:
grep [-acinv] [--color=auto] ‘查找字符串’ filename
参数:
-a 将binary文件以text文件的方式查找书籍
-c 计算找到’查找字符串’的次数
-i 忽略大小写
-n 输出行号
-v 反向选择,即显示出不符合的行
--color=auto 将找到的关键字部分加上颜色显示
 
例子:
last | grep ‘root’ | cut –d ‘ ’ -f 1
grep -n "conf" /etc/*  #找出文件含有conf字符串的行(文件)

6.2 排序命令:sort, wc, uniq

sort
对行进行排序,排序的字符与语系的编码有关,建议使用LANG=C来让语系统一。

用法:
sort [-fbMnrtuk] [file or stdin]
参数:
-f 忽略大小写
-b 忽略最前面的空格符
-M 以月份的名字来排序
-n 使用纯数字进行排序(默认以文字类型来排序)
-r 反向排序
-u 就是uniq,去重复
-t 分割符号,默认是用[Tab]来分隔
-k 以哪个区间来进行排序
 
例子:
cat /etc/passwd | sort -t ':' -k 3 –n
用冒号分隔取第三段当做纯数字来排序(-n 表示纯数字)

uniq
排序完成,将重复的行去掉。

用法:
uniq [-ic]
参数:
-i 忽略大小写
-c 进行计数
 
例子:
last | cut -d ‘ ‘ -f 1 | sort | uniq

wc

用法:
wc [-lwm]
参数:
-l 行数
-w 字数
-m 字符数

6.3 双向重定向: tee
tee会同时将数据流送到文件与屏幕,而输出到屏幕的,其实就是stdout,可以让下个命令继续处理。

用法:
tee [-a] file
参数:
-a 以累加(append)的方式将数据加入file当中
 
例子:
last | tee last.list | cut -d “ ” -f 1
利用tee保存一份数据到last.list并输出到屏幕。

6.4 字符转换命令:tr,col,join,paste,expand

tr

tr [-ds] SET1
参数:
-d 删除信息当中的SET1这个字符串
-s 替换掉重复的字符
 
例子:
last | tr ‘[a-z]’ ‘[A-Z]’
cat /root/passwd | tr –d ‘\r’

col

col [-xb]
参数:
-x 将tab键转换成对等的空格键
-b 在文字内有反斜杠(/)时,仅保留反斜杠最后接的那个字符

join

join [-ti12] file1 file2
参数:
-t join默认以空格符分割数据,并且对比第一个字段的数据,如两个文件相同,则将两条数据练成一行,且第一个字段放在第一个,-t指定分割符号
-i 忽略大小写
-1 数字1,代表第一个文件用药哪个字段来分析的意思
-2代表第二个文件用药哪个字段来分析
 
/etc/passwd的第四个字段是GID,哪个GID记录在/etc/group当中的第三个字段
join -t ‘:’ -1 4 /etc/passwd -2 3 /etc/group

相当关系数据库中的关联引用。注:在使用join之前,你所需要处理的文件应该要事先经过排序处理。

paste
直接将两行贴在一起,且中间以[tab]键隔开。

paste [-d] file1 file2
参数:
-d 后面可以接分隔符,模式是[tab]
- 如果file部分写成-,表示来自标准输入的数据
 
例子:
paste /etc/passed /etc/shadow
cat /etc/group | paste /etc/passwd /etc/shadow - | head –n 3
里面的横杆(-)表示来自cat的输出

expand
将[tab]按键转成空格键。

expand [-t] file
参数:
-t 后面可以接数字,一般,一个[tab]按键可以用8个空格键替换,也可以自行定义一个[tab]按键代表多少个字符。

6.5 切割命令:split
将大文件依据文件大小或行数来切割成小文件。

split [-bl] file PREFIX
参数:
-b 后接要切割成的文件大小,单位是b, k, m等
-l 以行数进行切割
PREFIX 代表前导符,可以作为切割文件的前导文字

6.6 参数代换:xargs
xargs再产生某个命令的参数。xargs可以读入stdin的数据,并且以空格符或断行字符进行分辨,将stdin的数据分割成arguments。使用xargs的原因是很多命令其实并不支持管道命令,因此可以通过xargs来提供该命令引用标准输入之用。

用法:
xargs [-0epn] command
参数:
-0 如果stdin含有特殊字符,这个参数可以将它还原成一般字符。
-e 这个是EOF意思。后面可以接一个字符串,当xargs分析道这个字符串时,就会停止工作。
-p 在执行每个命令的参数时,都会询问用户的意思
-n 后面接次数,每次command命令执行时,要使用几个参数的意思
 
例子:
cut –d ‘:’ –f1 /etc/passwd | xargs –p –n 5 finger
这样finger将从前面获取5个参数。
 
find /sbin –perm +7000 | xargs ls –l
通过xargs把前面的输出作为后面命令的参数,这个就是让ls原本不支持管道的命令间接支持管道的用法。

6.7 关于减号 – 的用途
在管道命令当中,经常会使用到前一个命令的stdout作为这次的stdin, 某些命令需要用到文件名来进行处理时,该stdin与stdout可以利用减号来替代(经过管道后前面的stdout就是后面的stdin,可以用-来引用前面的stdout或叫当前命令的stdin)。

tar –cvf – /home | tar –xvf –
前面把/home压缩到stdout,被管道带到下一个命令,下个命令使用减号引用管道带过来的前一个命令的stdout,作为其输入,然后再接压缩到stdout。

Bash Shell 正则表达式
1 基础正则表达式
1.1 语系对正则表达式的影响
使用正则表达式时,需要留意当时环境的语系,否则结果可能有差异。可以使用统一语系,比如LANG=C

[:alnum:]       英文大小写字符和数字,即0-9,a-z, A-Z
[:alpha:]       英文大小写字符,即a-z, A-Z
[:blank:]       空格和[Tab]
[:cntrl:]       键盘上控制键,即包括CR, LF, Tab, Del等
[:digit:]       数字,即0-9
[:graph:]       除了空格键以为所有按键
[:lower:]       小写字母
[:print:]       可以被打印的字符
[:punct:]       标点符号
[:upper:]       大写字母
[:space:]       空格
[:xdigit:]      十六进制的数字类型

1.2 基础正则表达式字符

^word        以word开头
word$        以word结尾
.        一个任意字符
\        转义字符
*        重复任意次
[list]       选取列出来的其中一个字符
[n1-n2]      选择n1到n2之间的一个字符,比如[0-9]
[^list]      选取排除了列出来的字符的其它任一字符
\{n,m\}      重复n次,最多不超m次,如果是\{n \}则表示重复n次,如果是\{n,\}则上重复n次以上

1.3 扩展正则表达式

+       重复一次或一次以上
?       重复零次或一次
|       用或的方式找出数个字符串
()      分组
()+     重复组

Bash Shell 工具- sed

rpm -qa | grep sed
sed-4.2.2-5.el7.x86_64

工具sed并不是Bash Shell的组成部分(grep cut等这些是内置命令),不过这个工具是Bash Shell编程不缺少的部分。

sed本身是一个管道命令,可以分析标准输入,而且sed还可以将数据进行替换、删除、新增、选取特定行等。

用法:
sed [-nefr] [动作]
参数:
-n 使用安静模式,一般,所有来自STDIN的数据都会列出到屏幕上,加入-n后,只有经过sed处理的那行才会被列出
-e 直接在命令行模式上进行sed的动作编辑
-f 直接将sed的动作写在一个文件内,-f filename则可以执行filename内的sed动作
-r sed的动作支持的是扩展正则语法
-i 直接修改读取的文件内容
 
动作说明:
[n1,[,n2]]function
n1, n2 选择进行动作的行数
 
function 参数
a 新增,a后可以接字符串
c 替换
d 删除
i  插入
p 打印
s 替换 通常可以搭配正则,如1,20s/old/new/g

例子:
1) 以行为单位的新增/删除
nl /etc/passwd | sed ‘2,5d’
将2~5行删除,d表示删除,sed后面接的动作务必用单引号括住,应该应该是sed –e,不用也可以。如果要删除3行以后所有行,nl /etc/passwd | sed ‘3,$d’,这个$表示最后一行,如果只要删除第二行,则nl /etc/passwd | sed ‘2d’

nl /etc/passwd | sed ‘2a vfeelit’
在第二行后加上vfeelit字符串,如果把a改成i,则是在第二行之前插入,如果要增加多行,每行后面需要使用转义字符分隔。

2) 以行为单位的替换与显示
nl /etc/passwd | sed ‘2,5c vfeelit’
这里的c表示替换,2到5行用给定的字符替换。

nl /etc/passwd | sed –n ‘5,7p’
这里的-n表示只有经过处理的打印出来(p),sed是把所有的都进行输出的,比如要列出11-20行,没有sed工具之前需要这样:head –n 20 | tail –n 10

3) 部分数据的查找并替换
sed的查找与替换与vi相当类似:
sed ‘s/查找字符串/新字符串/g’ 字母s前可以限定行数,比如2,10

cat /etc/man.config | grep ‘MAN’ | sed ‘s/#.*$//g’ | sed ‘/^$/d’

4) 直接修改文件内容
sed –s ‘s/\.$/\!/g’ regular.txt
直接在regular.txt中修改最后字符为.的修改为!

sed的”-i”参数可以直接修改文件内容。

Bash Shell 工具- awk

rpm -qa | grep awk
gawk-4.0.2-4.el7.x86_64

awk主要是处理每一行的字段内的数据,而默认的字段的分隔符为空格或[tab]键。

用法:
awk ‘条件类型1 {动作1} 条件类型2 {动作2} ..’ filename

注:注意其严格的格式,awk后接单引号,动作需要用大括号括起来。
例子:
last –n 5 | awk ‘{print $1 “\t” $3}’
显示每一行的第一和第三字段。

每一行的每个字段都是有变量名称的,那就是$1,$2 … 等变量名称。还有$0,表示整行数据的意思。

整个awk处理流程:
-读入一行,将数据填入$0,$1,$2这些变量中
-依据条件类型,判断是否执行后面的动作
-做完所有条件和动作
-如果还有后续行,重复

awk以行为单位,以字段为最小处理单位。至于数据有几行几列,则需要awk内置变量的支持:

NF      每一行拥有的字段总算
NR      带便目前处理的是第几行
FS      目前的分隔字符,默认是空格键

例子:
last –n 5 | awk ‘{print $1 “\t lines: “ NR “\t coleus:” NF}’

awk的逻辑运算符跟一般的没有差别,==表示判断是否等于,赋值使用=
例子:

cat /etc/passwd | awk ‘{FS=”:”} $3<10 {print $1 “\t ” $3}’

这里第一行没有正确显示,因为读入第一行时,那些$1,$2变量还是以空格作为分隔的,虽然用来FS=”:”,但是是从第二行开始生效的。可以使用BEGIN这个关键字预先设置awk的变量。

cat /etc/passwd | awk ‘BEGIN {FS=”:”} $3<10 {print $1 “\t ” $3}’

几个重要说明:
所有awk的动作,即在{}内的动作,如果有需要多个命令辅助时,可利用分号分隔
逻辑运行如果是等于判断务必使用==
格式化输出时,在printf的格式设置当中,务必加上\n才能进行分行
与bash shell变量不同,在awk中,变量可以直接使用,不需要加上$符号

Bash Shell 脚本
1 Shell Script入门
Shell Script编写注意事项:
1) 命令的执行从上而下,从左而右地分析与执行
2) 命令、参数间的多个空白会被忽略,空白行也被忽略,[tab]按键被视为空格键
3) 如果读取到一个Enter符号(CR),就尝试开始执行命令(以行为分隔)
4) #符号为注释

执行Shell Script方法:
1) 直接执行:Shell Script文件必须要具备可读可执行的权限,然后:
使用绝对路径来执行 比如/root/shell.sh
使用相对路径来执行 比如./shell.sh
变量PATH功能,将shell.sh放在PATH指定的目录内,如/bin/,然后让shell自动搜索PATH获取这个脚本并执行

2) 以bash进程来执行:通过bash shell.sh 或sh shell.sh来执行
如果shell.sh在/bin内具有rx的权限,那就直接输入shell.sh即可执行该脚本程序。因为/bin/sh是/bin/bash的链接文件,使用sh就是间接使用bash,此时shell.sh只要有r的权限即可被执行。

Shell Script组成部分
1) 第一行#!/bin/bash 声明这个script使用的shell名称,这样就能根据声明加载Shell(这里是bash)的相关环境配置文件(一般来说就是非登录Shell的~/.bashr)
2) 程序说明部分,以#开头,添加必要的说明信息
3) 主要环境变量声明
4) 程序部分
5) 告知执行结果
一个命令的执行是否成功,可以使用$?这个变量来查看。那么也可以利用exit这个命令来让程序中断,并且回传一个数值给系统。比如exit 0,这代表离开Script并且回传一个0给系统,如果执行echo $?则可得到0的值。利用exit n (n是数字)的功能,可以自定义错误信息。

范例:
1) 交互式脚本:变量内容由用户决定

#!/bin/bash
read –p “Please input you full name: ” fullname #提示用户输入
echo –e “\n Your full name is: $fullname”

2) 数值运算:简单的加减乘除
用declare来把变量定义成整数后才能进行加减运行,此外还可以利用$((计算式))来进行数值运算。Bash Shell里面仅支持整数。

#!/bin/bash
#
firstnu=10
secnu=6
total=$(($firstnu*$secnu))
echo –e “\n The result is: $total”

在数值运行上,也可以使用declare –i total=$firestnu*$secnu。

Shell Script的执行方式区别
脚本的执行除了以上提到的方式还可以利用source或小数点来执行。

1) 利用直接执行的方式来执行
直接命令执行(绝对路基、相对路径或$PATH内),或利用bash/sh来执行脚本时,该脚本都会使用一个新的bash环境来执行脚本内的命令。当子进程完成后,子进程内的各项变量或操作将会结束而不会传回到父进程中。
2) 利用source来执行脚本:在父进程中执行

2 判断式
2.1 利用test命令的测试功能
以下只列常用:

关于某个文件的“文件类型”判断,如test –e feliename表示是否存在

-e 		该文件是否存在
-f		该文件是否存在并且是文件
-d		该文件是否存在并且是目录

关于文件权限的检测,如test –r filename表示是否可读(但root除外)

-r		检测文件是否存在并且可读
-w 		检测文件是否存在并且可写
-x 		检测文件是否存在并且可执行
-u 		检测文件是否存在并且具有SUID属性
-g 		检测文件是否存在并且具有SGID属性
-k 		检测文件是否存在并且具有Sticky bit属性

关于两个整数之间的判断,如test n1 –eq n2

-eq		两数值相等 equal
-ne		两数值不等 not equal
-gt		n1大于n2 greater than
-lt		n1小于n2 less than
-ge		n1大于等于n2 grater than or equal
-le		n1小于等于n2 less than or equal

判定字符串的数据

test –z string	判定字符串是否为0,若string为空字符串,则为true
test –n string	判定字符串是否为非0,若string为空字符串,则为false
test str1=str2	判定str1是否等于str2,若相等,则回传ture
test str1!=str2	判定str1是否不等于str2,若相等,则回传false

多重条件判定,如test –r filename –a –x filename

-a		两个条件同时成立
-o		其中一个条件成立
!		取反,如test ! –x file,当file不具有x时,回传true

2.2 利用判断符号[]
可以使用[]来替代test,如[ -z “$HOME” ]; echo $?
注:在bash中用中括号作为shell的判断式时,必须要注意中括号的两端需要有空格符来分隔,如[空格”$HOME”空格==空格”$MAIL”空格]

2.3 Shell Script的默认变量($0,$1….)

Script针对参数已经设置好一些变量名称:

/path/to/scriptname  opt1  opt2  opt3
$0	             $1    $2    $3

执行的脚本文件名为$0变量,第一个接的参数就是$1。除此,还有一些特殊变量:

$#		代表后接的参数个数
$@		代表”$1”、 ”$2”、 ”$3”之意,每个变量是独立的(用双引号括起来)
$* 		代表”$1c$2c$3c$4”,其中c为分隔字符,默认为空格键

shift:造成参数变量号码偏移(**)
shift会移动变量,而且shift后面可以接数字,代表拿掉最前面几个参数的意思。

3 条件判断式
3.1 利用if..then
单层、简单条件判断式

if [ 条件判断式 ]; then
    内容
fi

如果有多个条件要判断时,可以有多个中括号来隔开,括号之间以&&或||隔开:
&&代表AND, ||代表or
比如:[ “$yn” == ”Y” –o “$yn” == ”y” ] 可以替代为:[ “$yn” == ”Y” ] || [ “$yn” == ”y” ]

多重、复杂条件判断式

if [ 条件判断式 ]; then
else
fi
 
if [ 条件判断式一 ]; then
elif [ 条件判断式二 ]; then
else
fi

elif也是个判断式,因此出现elif后面都要接then来处理。

3.2 利用case..esac判断
语法:

case $变量名称 in
    “第一个变量内容”)
        程序段
        ;;
    “第二个变量内容”)
        程序段
        ;;
    *)                  #最后一个变量内容都会用*来代表所有权其它值
        程序段
        exit 1
        ;;
esac

3.3 利用funtion功能
函数可以在Shell Script当中做出一个类似自定义执行命令的东西,因为Shell Script的执行方式是由上而下、由左而右的,因此在Shell Script当中的function的设置一定要在程序的最前面。

另外,function也是拥有内置变量的,它的内置变量与Shell Script很类似,函数名称代表$0,而后续接的变量也是以$1$2..来替代的(如果要向函数传递参数,对应位置的参数将替换$1$2..,比如一个函数叫getList,传递参数格式: getList aaa bbb, 这个函数内$1等于aaa,$2等于bbb)。

4 循环
4.1 while do done, until do done (不定循环)

while [ condition ]
do
    程序段
done
 
until [ condition ]
do
        程序段
done

#计算1+2+3+…+100
#!/bin/bash
#
s=0;
i=0
while [ “$i” != “100” ]
do
    i=$(($i+1))
    s=$(($s+$i))
done
echo “The result is: $s”

4.2 for..do..done(固定循环)

for var in con1 con2 con3 …
do
    程序段
done

4.3 for…do…done的数值处理

for ((初始值; 限制值; 执行步长))
do
    程序段
done

###
for ((i=1; i<=$nu; i=i+1))
do
    s=$(($s+$i))
done

4.4 Shell Script的追踪与调试

用法;
sh [-nvx] script.sh
参数:
-n 不要执行script,仅检查语法
-v 在执行script前先将其内容输出到屏幕上
-x 将使用到的script内容显示到屏幕上

由sh –x的方式来将命令执行过程也显示出来,如此用户可以判断程序代码执行到哪一段时会出现相关信息。

MongoDB SHELL使用

MongoDB提供的SHELL是一个Javascript Shell,实际想象一下MySQL的Shell,它可以认为是一个SQL Shell,可以执行SQL语句,Javascript Shell自然可以执行Javascript脚本。

启动Shell:

mongo
mongo 192.168.1.168:9999/vdb
mongo --host 192.168.1.168 –u admin –p 123456 –authenticationDatabase vdb

注意,如果不指定数据库,那么默认Shell会链接MongoDB服务器的test数据库,并将数据库链接赋值给全局变量db。这个变量是通过shell访问MongoDB的主要入口点。

如下:

###终端可用命令
> help
	db.help()                    help on db methods
	db.mycoll.help()             help on collection methods
	sh.help()                    sharding helpers
	rs.help()                    replica set helpers
	help admin                   administrative help
	help connect                 connecting to a db help
	help keys                    key shortcuts
	help misc                    misc things to know
	help mr                      mapreduce

	show dbs                     show database names
	show collections             show collections in current database
	show users                   show users in current database
	show profile                 show most recent system.profile entries with time >= 1ms
	show logs                    show the accessible logger names
	show log [name]              prints out the last segment of log in memory, 'global' is default
	use <db_name>                set current database
	db.foo.find()                list objects in collection foo
	db.foo.find( { a : 1 } )     list objects in foo where a == 1
	it                           result of the last line evaluated; use to further iterate
	DBQuery.shellBatchSize = x   set default number of items to display on shell
	exit                         quit the mongo shell

针对数据库 和 数据集合的可以用操作

> db.help()
DB methods:
	db.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ j
	db.auth(username, password)
	db.cloneDatabase(fromhost)
	db.commandHelp(name) returns the help for the command
	db.copyDatabase(fromdb, todb, fromhost)
	db.createCollection(name, { size : ..., capped : ..., max : ... } )
	db.createUser(userDocument)
	db.currentOp() displays currently executing operations in the db
	db.dropDatabase()
	db.eval(func, args) run code server-side
	db.fsyncLock() flush data to disk and lock server for backups
	db.fsyncUnlock() unlocks server following a db.fsyncLock()
	db.getCollection(cname) same as db['cname'] or db.cname
	db.getCollectionInfos()
	db.getCollectionNames()
	db.getLastError() - just returns the err msg string
	db.getLastErrorObj() - return full status object
	db.getLogComponents()
	db.getMongo() get the server connection object
	db.getMongo().setSlaveOk() allow queries on a replication slave server
	db.getName()
	db.getPrevError()
	db.getProfilingLevel() - deprecated
	db.getProfilingStatus() - returns if profiling is on and slow threshold
	db.getReplicationInfo()
	db.getSiblingDB(name) get the db at the same server as this one
	db.getWriteConcern() - returns the write concern used for any operations on th
	db.hostInfo() get details about the server's host
	db.isMaster() check replica primary status
	db.killOp(opid) kills the current operation in the db
	db.listCommands() lists all the db commands
	db.loadServerScripts() loads all the scripts in db.system.js
	db.logout()
	db.printCollectionStats()
	db.printReplicationInfo()
	db.printShardingStatus()
	db.printSlaveReplicationInfo()
	db.dropUser(username)
	db.repairDatabase()
	db.resetError()
	db.runCommand(cmdObj) run a database command.  if cmdObj is a string, turns it
	db.serverStatus()
	db.setLogLevel(level,<component>)
	db.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all
	db.setWriteConcern( <write concern doc> ) - sets the write concern for writes 
	db.unsetWriteConcern( <write concern doc> ) - unsets the write concern for wri
	db.setVerboseShell(flag) display extra information in shell output
	db.shutdownServer()
	db.stats()
	db.version() current version of the server

> db.test.help()
DBCollection help
	db.test.find().help() - show DBCursor help
	db.test.count()
	db.test.copyTo(newColl) - duplicates collection by copying all documents to newColl; no indexes are copied.
	db.test.convertToCapped(maxBytes) - calls {convertToCapped:'test', size:maxBytes}} command
	db.test.dataSize()
	db.test.distinct( key ) - e.g. db.test.distinct( 'x' )
	db.test.drop() drop the collection
	db.test.dropIndex(index) - e.g. db.test.dropIndex( "indexName" ) or db.test.dropIndex( { "indexKey" : 1 } )
	db.test.dropIndexes()
	db.test.ensureIndex(keypattern[,options])
	db.test.explain().help() - show explain help
	db.test.reIndex()
	db.test.find([query],[fields]) - query is an optional query filter. fields is optional set of fields to return.
	                                              e.g. db.test.find( {x:77} , {name:1, x:1} )
	db.test.find(...).count()
	db.test.find(...).limit(n)
	db.test.find(...).skip(n)
	db.test.find(...).sort(...)
	db.test.findOne([query])
	db.test.findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 'new': false] } )
	db.test.getDB() get DB object associated with collection
	db.test.getPlanCache() get query plan cache associated with collection
	db.test.getIndexes()
	db.test.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )
	db.test.insert(obj)
	db.test.mapReduce( mapFunction , reduceFunction , <optional params> )
	db.test.aggregate( [pipeline], <optional params> ) - performs an aggregation on a collection; returns a cursor
	db.test.remove(query)
	db.test.renameCollection( newName , <dropTarget> ) renames the collection.
	db.test.runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name
	db.test.save(obj)
	db.test.stats({scale: N, indexDetails: true/false, indexDetailsKey: <index key>, indexDetailsName: <index name>})
	db.test.storageSize() - includes free space allocated to this collection
	db.test.totalIndexSize() - size in bytes of all the indexes
	db.test.totalSize() - storage allocated for all data and indexes
	db.test.update(query, object[, upsert_bool, multi_bool]) - instead of two flags, you can pass an object with fields: upsert, multi
	db.test.validate( <full> ) - SLOW
	db.test.getShardVersion() - only for use with sharding
	db.test.getShardDistribution() - prints statistics about data distribution in the cluster
	db.test.getSplitKeysForChunks( <maxChunkSize> ) - calculates split points over all chunks and returns splitter function
	db.test.getWriteConcern() - returns the write concern used for any operations on this collection, inherited from server/db if set
	db.test.setWriteConcern( <write concern doc> ) - sets the write concern for writes to the collection
	db.test.unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the collection

Mongo SHELL 和 数据库 和 数据集合对应的原型是Mongo 和 DB 和 DBCollection:

> Mongo
function Mongo() { [native code] }
> DB
function DB() { [native code] }
> DBCollection
function DBCollection() { [native code] } 

根据Javascript的特征,如果我们要屏蔽某些方法,只要通过它的prototype来操作即可(这里的DB和db实际是类和类对象的关系):

var no = function(){};
db.dropDatabase = DB.prototype.dropDatabase = no;
DBCollection.prototype.drop = no;
DBCollection.prototype.dropIndex = no;

通过db.getCollection(cname) or db[‘cname’] or db.cname获取集合时,实际是一个DBCollection的类对象,所以在之前更改了原型,这个后生成的对象的某些方法就不能使用。

对比PHP链接MongoDB的代码:

$client = new MongoClient();				//Mongo 连接器
$db = $client->selectDB(“test”);		//DB

$collection = $db->selectCollection(“vvv”); //DBCollection

$collection->find();

第一步类似打开SHELL(指定主机等), 第二步类似SHELL中的选择数据库被赋值给db,第三步类似db.getCollection(cname)。大概可以看出,PHP驱动提供的名称与操作方法基本类似。

SHELL操作:

> db
test
> show dbs
comedy  0.031GB
local   0.031GB
test    0.031GB
> use comedy
switched to db comedy
> db
comedy
> show collections
blog
system.indexes
> db.blog.find()
{ "_id" : ObjectId("55442c13fe840f662522570b"), "id" : 1, "title" : "MongoDB Begin", "content" : "-----" }

由于Mongo SHELL也是一个JS解析器,可以加载外部的JS文件执行:

load(“jscrip.js”)

需要知道,除了Mongo SHELL引入的新内容外,JS语言的东西都可以直接运行,比如上面的load()就是一个本地函数。

Linux程序设计 – Shell程序设计

以下是“Linux程序设计”关于Shell程序设计的笔记,省略了一些内容。对于Shell的探讨趋向于简单罗列并且很粗糙,不是入门的材料。还是鸟哥的材料比较好,可以参考以下笔记:
Linux系统管理 – 使用vi/vim
Linux系统管理 – Bash Shell基础
Linux系统管理 – 正则表达式与文件格式化
Linux系统管理 – Shell Script

###管道和重定向

1 重定向输出

>
>>
2>
2>&1

2 从定向收入
3 管道

###作为程序设计语言的shell

1 交互式程序
2 创建脚本
脚本程序本质上被看作是shell的标准输入,所以他可以包含任何能够通过你的PATH环境变量引用到的Linux命令。

exit命令的作用是确保脚本程序能够返回一个有意义的退出码。

在shell程序设计里,0表示成功。因为这个脚本程序并不能检查到任何错误,所以它总是返回一个表示成功的退出码。

3 把脚本设置为可以执行

###Shell的语法

1 变量
直接赋值,全部看待成字符串,引用变量时需要使用美元符,区分大小写,特别,如果字符串包含空格,就必须用引号把它们括起来,等号两边不能有空格。

1) 使用引号
字符串通常都放在双引号中,以防止变量被空白字符分开,同时又运行使用美元符。使用单引号括起来的字符串,美元符的引用将不起作用。

2) 环境变量

$HOME	当前用户的家目录
$PATH	以冒号分割的用来搜索命令的目录列表
$PS1	命令提示符,通常是$字符...
$PS2	二级提示符,通常是>
$IFS	
$0	Shell脚本的名字
$#	传递给脚本的参数个数
$$	Shell脚本的进程号,脚本程序通常用它来生成一个唯一的临时文件,如/tmp/tmpfile_$$

3) 参数变量

$1, $2, ...	脚本程序的参数
$*		在一个变量中列出所有的参数,各个参数之间用环境变量IFS中的第一个字符分隔开。
$@		是$*的一种精巧变体 固定是用空格分隔

2 条件
一个shell脚本能够对任何可以从命令行上调用的命令的退出码进行测试。

test或[命令
当使用[时,还使用符合]来结束。如果把then和if放在同一行上,就必须要用一个分号把test语句和then分隔开:

if [ -f t.sh ]; then
...
fi

if [ -f t.sh ]
then
...
fi

if test -f t.sh; then
...
fi

if test -f t.sh
then 
...
fi




##字符串比较
string1 = string2   	###注意跟赋值的却别,赋值时等号两边不能有空格
string1 != string2
-n string		字符串是否为空,为空结果为真		
-z string		字符串是否为null(一个空串),为null则结果为真

##算术比较
exp1 -eq exp2
exp1 -ne exp2
exp1 -gt exp2
exp1 -ge exp2
exp1 -lt exp2
exp1 -le exp2
! exp

##文件条件测试
-d file			目录
-e file			目录或文件
-f file			文件
-g file			SGID
-r file			可读
-s file			文件大小不为0
-u file			SUID位
-w file			可写
-x file			可执行

3 控制结构
1)if语句
2)elif语句
3)一个与变量有关的问题
考虑如下代码:

if [ $timeofday = "yes" ]

当timeofday变量没有设置时将得到

if [  = "yes" ]

这里会提示错误。所以为了避免这种情况,必须给变量加上引号。即使变量没有设置,也会得到:

if [ "" = "yes" ]

4) for语句

##把指直接在in后列出来
for foo in bar fud 43
do
    echo $foo
done

##使用通配符扩展for循环
for file in $(ls f*.sh); do
  lpr $file
done

5) while语句

read trythis
while [ "$trythis" != "secret" ]; do
 echo "try again"
 read trythis
done

6) until语句
7)case语句

read timeofday
case "$timeofday" in
  yes) echo "Good Morning";;
  y  ) echo "Good Morning"
  no ) echo "Good Afternoon";;
  *  ) echo "Sorry.";;
esac

##合并匹配模式
read timeofday
case "$timeofday" in
  yes | y | Yes | YES) echo "Good Morning";;
  no ) echo "Good Afternoon";;
  *  ) echo "Sorry.";;
esac

##执行多条语句
##合并匹配模式
read timeofday
case "$timeofday" in
  yes | y | Yes | YES) 
	echo "Good Morning"
	echo "Up bright and early this moring"
	;;
  no ) echo "Good Afternoon";;
  *  ) echo "Sorry.";;
esac

为了让case匹配功能更强大,可以使用如下模式:

[yY] | [Yy][Ee][Ss]

注意,如果*之后没有其它的匹配,那么它之后的;;是可以省略的。

8)命令列表(&& 和 ||)
9)语句块(使用{}括起来)

4 函数
当一个函数被调用时,脚本程序的位置参数($* $@ $# $1 $2等)会被替换为函数的参数。这也是读取传递给函数的参数的办法。当函数执行完毕后,这些参数会恢复它们先前的值。

可以通过return命令让函数返回数字值。或者可以把结果保存到一个变量中。或者用echo输出,然后捕获输出:

foo () { echo vfeelit; }
...
result="$(foo)"

如下程序描述函数参数如何传递(需要好好体会)

#!/bin/sh

yes_or_no() {
  echo "Is your name $* ?"  ##把传递进来的参数全部输出  $*不再是脚本传递进来的值,是当前函数里的被改变的值
  while true
  do
      echo -n "Enter yes or no: "
      read x
      case "$x"	in
  	y | yes ) return 0;;
	n | no ) return 1;;
	* ) echo "Answer yes or no"
      esac
  done
}

echo "Original parameters are $*"

if yes_or_no "$1"   ##把脚本的第一个参数传递给函数
then
  echo "Hi $1, nice name"
else
  echo "Never mind"
fi

exit 0

5 命令
1) break
2) :命令
冒号(:)命令是一个空命令。它偶尔会被用于简化条件逻辑,相当于true的一个别名。
3) continue命令
4).命令
执行一个shell脚本将使得脚本在一个子shell中运行,如果希望它就在当前shell里执行,可以使用点命令。
5)echo命令

foo=10
x=foo
y='$'$x
echo $y
#以上直接输出字符串$foo

foo=10
x=foo
eval y='$'$x
echo $y
#以上输出10,y='$'$x被解析为y=$foo这个字符串,这时eval把这个字符串当做脚本解析,意思是y被赋值10,所以最后输入10

7)exec命令
8)exit n命令
exit命令使脚本程序以退出码n结束运行。如果你允许自己的脚本程序在退出时不制定一个退出状态,那么该脚本中最后一条被执行命令的状态将被用作为方回值。
9)export命令
将换一个变量变成环境变量
10) expr命令
expr命令将它的参数当作一个表示式来求值。

exp1 | exp2
exp1 & exp2
exp1 = exp2
exp1 > exp2
exp1 >= exp2
exp1 < exp2
exp1 <= exp2
exp1 != exp2
exp1 + exp2
exp1 - exp2
exp1 * exp2
exp1 / exp2
exp1 % exp2

在较新的脚本程序中,expr命令通常被替换为更加有效的$((…))语法。

11) printf命令
12)return命令
13)set命令
set命令的作用是为shell设置参数变量(映射到$引用中)。许多命令的输出结果是以空格分隔的值,如果需要使用输出结果中的某个域,这个命令非常有用。
14)shift命令
shift命令把所有参数变量左移一个位置,使$2变成$1,$3变成$2,以此类推。原来$1的值将被丢弃,$0将保持不变。如果调用shift命令时指定了一个数值参数,则表示所有的参数将左移制定的次数。

15)trap命令
trap命令用于指定在接收到信号后将要采取的行动。trap命令的一种常见用途是在脚本程序被中断时完成清理工作。
16)unset命令

永久链接:http://blog.ifeeline.com/1009.html