变量

定义和使用变量

一般如果某个内容会在之后的脚本里反复用到,此时我们应该把其抽象成变量,比如写个备份MySQL数据的脚本,显然MySQL的IP地址和端口应该提出来作为变量

myip=192.168.1.1   && echo $myip
myip=192.168.1.1   && echo ${myip}
myip="192.168.1.1" && echo ${myip}qq
myip="192.168.1.1" && echo '${myip}qq'
myip="192.168.1.1" && echo "${myip}qq"

path=`pwd`
path=$(pwd)

FZS_PATH=/data/fzs
cp -a ${FZS_PATH}/save/1/powerfile ${FZS_PATH}/save/1/powerfile.`date | tr " :" "-"` 

变量名是由数字,字母,下划线组成,不能有其它字符,数字不能作为变量的开头

单引号原样输出,双引号解析变量,这是最基本的判断,不过由于shell脚本的书写方式非常灵活,有些时候各种单双引号互相嵌套很容易搞懵,尤其再碰到想再三剑客命令里使用shell的变量,更是复杂了,此时多注意用bash -x name.sh的方式,仔细调试,总是可以搞定的

环境变量 (系统变量)

1 如何创建环境变量 ?
[root@xingyongsheng ~]# cat /etc/profile | grep export
    export HISTCONTROL=ignoreboth
    export HISTCONTROL=ignoredups
如上,可以看到,在定义变量的时候前面加上 export 即可,通常习惯上使用大写字母定义环境变量,还可以使用declare命令来定义环境变量
    declare -x NAME=hello

2 环境变量的有效范围 ?
普通变量是只在当前脚本内生效,而环境变量会在全局范围内都生效,所有的脚本都可以使用系统的环境变量

3 常见的系统环境变量配置文件有哪些 ?
/etc/profile
/etc/bashrc 
~/.bash_profile 
~/.bashrc
/etc/profile.d/
若要在登录后初始化或显示加载内容,则把脚本文件放在/etc/profile.d/下即可(无须加执行权限)

4 如何查看系统里有哪些环境变量 ?
set env declare
set        输出所有变量,包括局部变量和全局变量
env        只显示全局变量  OR printenv
declare    输出所有的变量、函数、整数和已经导出的变量
set -o     显示bash shell所有参数的配置信息

5 常见的系统环境变量含义
HISTFILE        命令记录记录历史文件的全路径
HISTFILESIZE    历史命令文件记录的最大行数
HOME            当前用户的家目录
HOSTNAME        当前主机名称
IFS             内部字段分隔符
LANG            字符集
MAIL            邮件路径
PATH            命令执行路径
SHELL           当前使用的SHELL

6 如何引用另一个脚本里定义的变量
source commonEnv.sh

系统内建的 function

function hello() {
    date
    echo "hello" 
}
hello

上面的函数我们直接在命令中敲击后面即可直接用 hello ,相当于自定义一个函数当命令用, 如果我们想让其重启也能生效,可以放到 /etc/profile.d/

那么问题来了,如果我们全新拿到一台机器,如何知道这台机器上有哪些函数可以用呢 ?
使用 env | grep hello 是不行的,可以使用
type hello
typeset (列出全部变量和函数)
======================= 冗余输出参考 =======================
[root@node3 ~]# type hello
hello is a function
hello ()
{
    date;
    echo "hello"
}

特殊的位置参数变量

$1 $2 $3 ... ${10} ${11}
$0
$#
$*
$@



################   $1 $2 $3 ... ${10} ${11} 实践  #######################
$ cat hello.sh 
echo $1 $2
(( sum=$1+$2 ))
echo $sum
$ bash hello.sh 2 5
2 5
7



###############   $0 dirname basename 实践  #################################
$ cat /home/dc2-user/dir/child/hello.sh
echo $0
$ cd /home/dc2-user/dir/child && bash hello.sh 
hello.sh
$ cd /home
$ bash ./dc2-user/dir/child/hello.sh 
./dc2-user/dir/child/hello.sh
$ bash dc2-user/dir/child/hello.sh 
dc2-user/dir/child/hello.sh
通过上面的例子我们可以看到 $0 是打印当前执行脚本的全路径,输出的结果与我们如何表示文件路径的方式有关(绝对路径 相对路径 等)

如果我们想要单独拿到执行的目录或者文件名的部分,就需要用到 dirname 和 basename
$ dirname /home/dc2-user/dir/child/hello.sh
/home/dc2-user/dir/child
$ basename /home/dc2-user/dir/child/hello.sh
hello.sh
$ cd /home && basename dc2-user/dir/child/hello.sh
hello.sh
$ cd /home && dirname dc2-user/dir/child/hello.sh
dc2-user/dir/child



#############################  $# 位置参数实践  ###################################
$ cat q.sh
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}
echo $#
$ bash q.sh {1..20}
1 2 3 4 5 6 7 8 9 10 11
20
可以看到 $# 是用来确定实际传入脚本的参数个数,比如某些时候我们写的脚本要求必须传入2个参数,不能多不能少,我们就可以利用这个 $# 来实现,如下
$ cat q2.sh
if [ $# -ne 2 ]; then
    echo "USAGE: /bin/bash $0 arg1 arg2"
    echo "参数必须是两个"
    exit
fi
echo "bula bula bula"
$ bash  q2.sh 
USAGE: /bin/bash q2.sh arg1 arg2
参数必须是两个
$ bash  ~/q2.sh 
USAGE: /bin/bash /home/dc2-user/q2.sh arg1 arg2
参数必须是两个

特殊的状态变量

$?   上一次执行命令的返回值,0表示成功,非0表示失败
$$   获取当前执行的shell脚本的进程PID
$!   获取上一个在后台工作的进程PID
$_   获取在此之前执行的命令或脚本的最后一个参数

可用过 man bash ,搜索 Special Parameters 找到相关帮助

###########################   $? 实践   #####################################
这个特殊变量是比较常用的,比如我们想要在脚本中确认,当前机器是否能上网
$ ping -c 3 -i 1 baidu.com
PING baidu.com (220.181.38.148) 56(84) bytes of data.
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=54 time=4.13 ms
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=2 ttl=54 time=3.94 ms
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=3 ttl=54 time=2.69 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 4211ms
rtt min/avg/max/mdev = 2.699/3.595/4.139/0.638 ms
$ echo $?
0
$ if [ $? -eq 0 ]; then 
    echo "可以上网"
else
    echo "不能上网"
fi

下面的测试是测试当前机器是否能连通192.168.1.232,通过 $? 输出是1,判断当前机器不能连通192.168.1.232
$ ping -c 3 -i 1 192.168.1.232
PING 192.168.1.232 (192.168.1.232) 56(84) bytes of data.
--- 192.168.1.232 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms
$ echo $?
1



###########################   $$ 实践   #####################################
有时我们有这样的需求,我们写的脚本是需要在后台一直运行的,比如搭建个基于WEB界面的文件上传服务器,给大家上传下载文件用,这种类型的脚本满足两个特征
1 需要放到后台一直执行 (配合 nohuo & 实现)
2 可能需要经常重启,也就是这个脚本会多次反复执行 (这里我们就要自己保证每次执行前kill掉上一次运行的进程)
这种时候我们可以在运行这个脚本的时候尽量使用绝对路径,这样后面过滤进程PID的时候不会造成困然,另外一种比较方便的方法,就是利用 $$
[dc2-user@10-255-20-218 ~]$ cat hello.sh 
echo $$
sleep 300
[dc2-user@10-255-20-218 ~]$ nohup bash hello.sh &> /tmp/tmp.out &
[1] 23512
[dc2-user@10-255-20-218 ~]$ ps -ef | grep 23512
dc2-user 23512 19141  0 11:00 pts/0    00:00:00 bash hello.sh
dc2-user 23513 23512  0 11:00 pts/0    00:00:00 sleep 300
dc2-user 23531 19141  0 11:00 pts/0    00:00:00 grep --color=auto 23512
[dc2-user@10-255-20-218 ~]$ ps -ef | head -1
UID        PID  PPID  C STIME TTY          TIME CMD

BASH SHELL 常用内置命令

如 echo export 这样的命令是BASH SHELL自带的,这样的命令我们一般用 help echo 的方式查看其帮助,可通过 man help 查看这一类的命令

echo 输出内容



eval args 当shell程序执行到eval语句时,shell读入参数args,并将它们组合成一个新的命令,然后执行



exec args 命令能够在不创建新的子进程的前提下,转去执行指定的命令,当指定的命令执行完毕后,该进程(也就是最初的shell)就终止了,如下的方式实现了按行读取文件并处理
seq 5 > /tmp/tmp.log
cat exec.sh
exec < /tmp/tmp.log
while read line; do
    echo "$line XXX"
done
上面这个exec的骚操作到底有没有实际场景呢?给大家分享一个我在实际工作中遇到的一个问题,有一个文本文件里面的内容时不断增加的,我需要对每新增的一行做处理



read 从标准输入读取字符串等信息,传给shell程序内部定义的变量



shift shift语句会按如下方式重新命名所有的位置参数变量,即$2成为$1、$3成为$2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止



exit  exit the shell



[dc2-user@10-255-20-218 ~]$ time free
              total        used        free      shared  buff/cache   available
Mem:        1882212      168296     1120616       41448      593300     1495132
Swap:             0           0           0

real	0m0.009s
user	0m0.002s
sys	    0m0.006s
[dc2-user@10-255-20-218 ~]$ time df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G  1.9G   39G   5% /
devtmpfs        897M     0  897M   0% /dev
tmpfs           920M     0  920M   0% /dev/shm
tmpfs           920M   41M  879M   5% /run
tmpfs           920M     0  920M   0% /sys/fs/cgroup
tmpfs           184M     0  184M   0% /run/user/0
tmpfs           184M     0  184M   0% /run/user/1000

real	0m0.007s
user	0m0.003s
sys	    0m0.002s

变量子串 (Parameter Expansion)

man bash 搜索 Parameter Expansion

   Parameter Expansion
       The  `$'  character introduces parameter expansion, command substitution, or arithmetic expansion.  The parameter name or symbol to be expanded may be enclosed in braces, which are optional
       but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.

       When braces are used, the matching ending brace is the first `}' not escaped by a backslash or within a quoted string, and not within an embedded arithmetic expansion, command substitution,
       or parameter expansion.

       ${parameter}
              The  value of parameter is substituted.  The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character which
              is not to be interpreted as part of its name.

       If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced.  Bash uses the value of the variable formed from the rest of parameter as the
       name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself.  This is known as indirect expansion.
       The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below.  The exclamation point must immediately follow the left brace in order to introduce indirection.

       In each of the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.

       When not performing substring expansion, using the forms documented below, bash tests for a parameter that is unset or null.  Omitting the colon results in a test only for a parameter  that
       is unset.

       ${parameter:-word}
              Use Default Values.  If parameter is unset or null, the expansion of word is substituted.  Otherwise, the value of parameter is substituted.
       ${parameter:=word}
              Assign Default Values.  If parameter is unset or null, the expansion of word is assigned to parameter.  The value of parameter is then substituted.  Positional parameters and special
              parameters may not be assigned to in this way.
       ${parameter:?word}
              Display Error if Null or Unset.  If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard  error  and  the
              shell, if it is not interactive, exits.  Otherwise, the value of parameter is substituted.
       ${parameter:+word}
              Use Alternate Value.  If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
       ${parameter:offset}
       ${parameter:offset:length}
              Substring  Expansion.   Expands  to  up  to length characters of parameter starting at the character specified by offset.  If length is omitted, expands to the substring of parameter
              starting at the character specified by offset.  length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below).  If offset evaluates to a number less than  zero,  the
              value is used as an offset from the end of the value of parameter.  If length evaluates to a number less than zero, and parameter is not @ and not an indexed or associative array, it
              is interpreted as an offset from the end of the value of parameter rather than a number of characters, and the expansion is the characters between the two offsets.  If  parameter  is
              @,  the result is length positional parameters beginning at offset.  If parameter is an indexed array name subscripted by @ or *, the result is the length members of the array begin‐
              ning with ${parameter[offset]}.  A negative offset is taken relative to one greater than the maximum index of the specified array.  Substring  expansion  applied  to  an  associative
              array produces undefined results.  Note that a negative offset must be separated from the colon by at least one space to avoid being confused with the :- expansion.  Substring index‐
              ing is zero-based unless the positional parameters are used, in which case the indexing starts at 1 by default.  If offset is 0, and the positional parameters are used,  $0  is  pre‐
              fixed to the list.

       ${!prefix*}
       ${!prefix@}
              Names  matching prefix.  Expands to the names of variables whose names begin with prefix, separated by the first character of the IFS special variable.  When @ is used and the expan‐
              sion appears within double quotes, each variable name expands to a separate word.

       ${!name[@]}
       ${!name[*]}
              List of array keys.  If name is an array variable, expands to the list of array indices (keys) assigned in name.  If name is not an array, expands to 0 if name is set and null other‐
              wise.  When @ is used and the expansion appears within double quotes, each key expands to a separate word.

       ${#parameter}
              Parameter  length.   The  length  in  characters  of the value of parameter is substituted.  If parameter is * or @, the value substituted is the number of positional parameters.  If
              parameter is an array name subscripted by * or @, the value substituted is the number of elements in the array.

       ${parameter#word}
       ${parameter##word}
              Remove matching prefix pattern.  The word is expanded to produce a pattern just as in pathname expansion.  If the pattern matches the beginning of the value of  parameter,  then  the
              result  of the expansion is the expanded value of parameter with the shortest matching pattern (the ``#'' case) or the longest matching pattern (the ``##'' case) deleted.  If parame‐
              ter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list.  If parameter is an array variable  subscripted
              with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

       ${parameter%word}
       ${parameter%%word}
              Remove  matching suffix pattern.  The word is expanded to produce a pattern just as in pathname expansion.  If the pattern matches a trailing portion of the expanded value of parame‐
              ter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ``%'' case)  or  the  longest  matching  pattern  (the  ``%%''  case)
              deleted.   If  parameter  is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list.  If parameter is an array
              variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

       ${parameter/pattern/string}
              Pattern substitution.  The pattern is expanded to produce a pattern just as in pathname expansion.  Parameter is expanded and the longest  match  of  pattern  against  its  value  is
              replaced with string.  If pattern begins with /, all matches of pattern are replaced with string.  Normally only the first match is replaced.  If pattern begins with #, it must match
              at the beginning of the expanded value of parameter.  If pattern begins with %, it must match at the end of the expanded value of parameter.  If string is null,  matches  of  pattern
              are  deleted and the / following pattern may be omitted.  If parameter is @ or *, the substitution operation is applied to each positional parameter in turn, and the expansion is the
              resultant list.  If parameter is an array variable subscripted with @ or *, the substitution operation is applied to each member of the array in turn, and the expansion is the resul‐
              tant list.

       ${parameter^pattern}
       ${parameter^^pattern}
       ${parameter,pattern}
       ${parameter,,pattern}
              Case modification.  This expansion modifies the case of alphabetic characters in parameter.  The pattern is expanded to produce a pattern just as in pathname expansion.  The ^ opera‐
              tor converts lowercase letters matching pattern to uppercase; the , operator converts matching uppercase letters to lowercase.  The ^^ and ,, expansions convert each matched  charac‐
              ter  in  the  expanded  value;  the ^ and , expansions match and convert only the first character in the expanded value.  If pattern is omitted, it is treated like a ?, which matches
              every character.  If parameter is @ or *, the case modification operation is applied to each positional parameter in turn, and the expansion is the resultant list.  If  parameter  is
              an array variable subscripted with @ or *, the case modification operation is applied to each member of the array in turn, and the expansion is the resultant list.