----------------------------------------
回顾系统变量:
变量有4种类型:
1.本地变量。对子shell是无效的2.环境变量。 不但对当前shell有效,还对子shell有效的。
声明的时候一定要用 export 来声明,表示导出一个变量
export A=3 (直接导出)
或者
A=3
export A
Bash为用户准备了许多变量,以支持用户来调用 系统中到底有哪些环境变量呢?
export 不带任何选项的此命令,就能看出现在正在被使用的环境变量。
DISPLAY="" 用于定义你当前桌面在哪个位置显示,哪个屏幕显示
HISTSIZE=""命令历史当中可以保存过去多少条命令的 (需要记住的)
HOME= 用于表示当前用户的家目录的
HOSTNAME= 用于表示主机名
LANG= 主机语言
LOGNAME= 登录到当前用户的用户是
PATH= 外部命令查找的可执行文件的访问路径
PWD= 显示当前目录
OLDPWD= 其实就是 cd - 在上一个目录之间前换
SHELL= 当前shell
USER= 当前用户
PS1= 显示头部文件
当前环境变量的某些值来替换 \u 用户名 \h主机名 \W 基名 \$ 管理员用# 其他用户用$
\! 命令在历史行里是第多少条命令
\t 显示时间
恢复成原本
export PS1='[\u@\h \W]\$' (这是原有方式
特殊标识方法
'[\033[31m\u\033[0m@\h \W]\]$' 加颜色
\033其实是一种ASII码,控制颜色的
printenv 显示部分环境变量
env 表示你可以在一个指定的环境下运行某个命令
3.特殊变量。
也是Bash内置的变量,
最著名的变量:$?
用于表示上一个命令的执行结果,用于看上一个命令是否执行成功了。
在linux编程中,0 永远表示 上一个命令成功了,如果失败了,会用1-255表示。
一般来讲,只要是大于0,都是没有执行成功的。但每个错误会有不同的错误返回值。
4.位置变量。用这个变量本身的位置调用的
比如$1 $2 $3... . 可以直接向脚本调用参数并应用的
脚本中
echo "the first param: $1"
echo "the 2 param $2"
echo "the 3 param $3"
在命令行下 1sh.sh a b c 则直接将a b c 变为 $1 $2 $3
--------------------------
扩展:算术运算
A=2
A=3
echo "$[$A+$B]" 则显示结果
echo "$(($A+$B))" 用$加双括号,也可以显示
--------------------------
练习:想命令行传递两个任意整数并算出两个整数的和:
#!/bin/bash
echo "The sum is : $[$1+$2]"
保存,命令行
./sum.sh 4 5 : 则内部将4直接赋值给$1 ,5赋值给$2 并显示运算结果
--------------------------
BASH的命令别名:
我们可以给命令取上别名。
alias命令:
alias cls=clear 则可以将clear加一个别名为cls
alias cdnet='cd /etc/sysconfig/network-scripts' 则将CD这条命令简写成一个词
不想用alias的话
unalias 命令
比如
unalias cdnet.
*******环境变量,BASH别名什么的,只对当前变量生效,重启之后都没有了。通过命令的方式,仅仅是临时保存。
*******但是PS1等变量还是有效。因为我们把自己的文件保存到了系统的变量中,它将直接保存。
如果想让他们重启还生效。则需要修改相关的文件
刚刚装完系统之后,为了初始化所有用户的初始环境,系统有一个全局设置
而用户还有自有的特殊的,局部有效的配置文件。
全局的表示对所有用户有效
局部的表示对特有用户有效
当既有全局又有局部的时候,则局部生效
全局有效的配置文件:
/etc/profile ,
/etc/profile.d/* ,
/etc/bashrc
局部有效的配置文件们:
~/.bash_profile
~/.bashre,
~/.bash_.logout
这些文件分两类,
1是profile类
通常是用来设置环境变量的。
用来运行一些命令的(用户登录时要执行的命令)
2是bashrc类
一般来讲是用来设置别名的
用于设置本地变量
shell对用户来讲,分为两大类。
1 交互式登录式shell ,以一个用户的身份输入账号和密码之后直接输入命令的shell
运行次序:/etc/profile --> /etc/profile.d/* --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
2.非登录式shell :当运行一个脚本时系统自动运行的shell
运行次序:~/.bashrc --> /etc/bashrc --> /etc/profile.d/*
------------------------
PATH命令:声明在profile里。
很有可能需要手动进去声明它的脚本变量。
-------------------------
比如声明: alias cdnet 使之全局有效
nano /etc/bashrc 编辑 bashrc 文件
alias cdnet='cd /etc/sysconfig/network-scripts'
如果只想让一个用户有效则:进入用户家目录: ~/.bashrc里加入
----------------------------------
如何去声明一个环境变量:
比如定义环境变量
FAVORCOLOR=blue 使之对所有用户都有效
可以在/etc/profile里声明也可以在 /etc/profile.d/*里声明
etc/profile.d其实是为了减少 etc/profile的文件体积,并且让其管理起来方便
进入profile 最后
export FAVORCOLOR='Blue'
----------------------------------
任何用户是在读取这个变量的时候是在登录的那一刻读取的。
已经登录的用户并不受影响
-----------------------------------
如何使修改的登录变量立即有效?
使用source 命令,重读一下配置文件
source /etc/profile
使用“.” 命令,重读一下配置文件
其实 . = source
比如 . /etc/profile
不建议使用source ,会影响到其他变量的使用。
--------------------------------
小思考:想让用户登录进来时能够显示一句话:
Hi,I know you ,you are root.
让用户登录的时候加入一句:请注意个人言行
练习:写一个脚本,
传递两个整数给脚本,让脚本分别计算并显示这两个整数的和差积商
Bash不能做浮点运算
为了避免展开
echo '$5000' 可以在强引用里使用
也可以
echo "\$5000" 反斜杠,永远是为了避免展开的,避免转义的。
echo \" \" 则能将 “” 显示出来
----------------------------------
Shell的输入输出重定向功能,管道:
默认情况下,当你执行完一个命令,这个命令必然会输出一些信息,但是这个信息输出到了什么位置呢?
输入设备和输出设备有很多种,大多时候,进行命令时,可能都没有明确说明应该输出到什么地方去,或者从什么输入
当你不指定的时候,linux会输入输出到默认设备上去。
-------------------
标准输入:0, 一般指键盘 keyboard
/dev/stdin (标准输入设备,显示为浅绿色),
其实是一种链接文件。用ll可以看出它有指向。
大多数情况下,我们用0 来表示:文件描述符
重定向一般用 < 表示
比如
cat < /etc/fstab 则将fstab的内容输入到了cat下,并用cat命令显示
--------------------
标准输出: 1,默认是显示器 monitor
/dev/stdout
重定向一般用 1> 表示
比如
ls > /tmp/ls.out 则将ls的内容,输出到了 /tmp/ls.out
--------------------
错误输出: 2,默认是显示器 monitor
/dev/stderr
重定向一般用 2> 表示
比如:
lss 2> /tmp/ls2.out
但是当你这个信息是正确时候,它不会输出到指定,而是直接显示
---------------------
合并标准输出和错误输出的输出流:
" &> " 使用它可以合并。
---------------------
输出重定向会覆盖文件的原有内容,所以,
" > "号叫覆盖输出重定向
" >> "号叫追加输出重定向,可以追加内容,而不覆盖
很多时候,我们可以使用echo命令,往一个文件的最后一行追加一行文字
echo "-------------------" >> /tmp/ls.out
---------------------
当使用“2>>” 时可以追加错误重定向
但 "&>>"是不支持双大于号来追加所有内容的。
---------------------
有时会出现手误少打一个>导致,直接覆盖了原有内容而导致不得恢复
set -C 命令,打开了禁止使用覆盖重定向 ,想开启的话
set +C 命令。关闭禁止使用覆盖重定向
有时我们就是想覆盖文件的话。
">| "符号后面加一个竖线,则表示我清楚我在做什么,我非要覆盖。
---------------------
如何实现将正确的输出流保存在1.txt ,如果错误,则保存错误到2.txt呢?
ls /var >> /tmp/1.txt 2>> /tmp/2.txt
意思是将 ls /var的输出结果 正确的话追加到1.txt ,错误的话追加到 2.txt 只不过这两个符号要分开使用,中间加空格。
----------------------------------------------------
管道:" | "
COMMAND1 | COMMAND2 | COMMAND3....
管道的作用:把前一个命令的输入输出执行结果,当做第二个命令的输入命令来处理。
比如:
ls -l /etc | less
则是将 ls -l /etc 的输出结果,在less中输入,则可以使用less 查看
比如:
echo "123456" | passwd redhat
将123456的结果通过管道输送给 用户redhat的passwd中。
可以连接多个命令,不光是两个命令的链接。比如:
ls -l /etc | sort -t | less
反向排序之后送给less查看
既想送给less命令分页查看,又想保存一份到文件里去
ls -l /etc | tee /tmp/ls.out | less
tee:会把数据流分成两部分,一部分保存,一部分送给后面的命令
则,将数据先保存一份到ls.out,再用less命令查看
有多个命令的输出,都需要保存在同一文件。为了实现:
command >> a.out
command2 >> a.out
则可以使用一个变量,用脚本实现。但是麻烦,所以
自定义输出重定向:可以用(3-9)的数字
exec :exec 3> /tmp/myout.out (也可以用追加">>" 或者读入"<" )
则将3定向为了 myout.out 其实是覆盖为了这个文件
之后
ls /var >&3 (必须用 >& 符号)
则会将显示结果覆盖至 自定义的一个输出重定向 3中
在多次重定向的场景里,避免多次的反复重定向的那个文件而导致的频繁打开和关闭。
撤销:
exec 3>&-
这样就关闭了此文件
/dev/null:数据黑洞,设备文件的空文件。会把所有送来的数据吞噬掉。
我们可以实现把那些输出出来没用的数据,送到这里
这样不会占用任何空间。(属于虚拟设备)
====================================
程序执行流:
顺序执行:一般来说,程序执行的次序都是顺序执行的。(执行流的一种)
选择分支:选择一种特定的,条件满足时则执行的分支
循环执行:当你满足特定条件时,会反复的执行一遍又一遍直到条件不满足
===================================
循环执行:for
想在系统上加入10个用户,user1,user2.....user10。
在脚本编程中,使用
格式:
for I in LIST; do
statement1
statement2
done
第一次,它将I的值取为1,LIST1 之后执行一遍
之后将I的值取为 LIST2....
for I in 1 2 3 4 5 6 7 8 9 10; do
useradd user$I
echo user$I | passwd --stdin user$I
done
则,会自动添加user1-10并将其密码设置为自己的用户名。
--------为了方便--------
1. seq 1 10 : 表示,以1开始以10结束的数字展开为一个序列,但是使用的时候要用命令替换
比如
for I in `seq 1 10`; do
2. {1..10} :也表示取整数序列,从哪开始,从哪结束。
-------------------------
------------------------------------------------------
扩展练习:
ping 192.168.0.151--192.168.0.254的,每个ping 一次
ping 一次的方式: -c 1
写一个脚本:
1、切换工作目录至/var
2、依次向/var目录中的每个文件或子目录问好,形如:
(提示:for FILE in /var/*; 或for FILE in `ls /var`; )
Hello, log
Hello, run
3、统计/var目录下共有多个文件,并显示出来
解答:
#!/bin/bash
#
cd /var
for FILE in /var/*; do
echo "Hello, $FILE "
done
echo "There is `ls -l | wc -l` files."
-----------------------------
也可以最开始赋值SUM
let SUM=0
在循环里,
SUM=$[$SUM+1]
最后显示SUM就好。
写一个脚本:
1、设定变量FILE的值为/etc/passwd
2、使用循环读取文件/etc/passwd的第2,4,6,10,13,15行,并显示其内容;
(提示:LINE=`head -2 /etc/passwd | tail -1`可以取得第2行)
3、把这些行保存至/tmp/mypasswd文件中
#!/bin/bash
#
FILE=/etc/passwd
for I in 2 4 6 10 13 15; do
echo "LINE$I=`head -"$I" $FILE | tail -1`"
echo LINE$I=`head -"$I" $FILE | tail -1` >> /tmp/mypasswd
done
------------------------------
方法二
FILE='/etc/passwd'
exec 3>> /tmp/mypasswd #####自定义追加输出重定向
for I in 2 4 6 10 13 15; do
LINE=`head -$I $FILE | tail -1`
echo $LINE
echo $LINE >&3
done
exec 3>&- #####撤销输出重定向
unset FILE LINE #####撤销变量
=============下=====午================
文本处理类命令:
cut : 将文本内容一行一行的按照某种格式给他剪裂开来
-d :用于指定分隔符。“d:”以“:”分割
-f : 用于指定取第几段 -f1 则为第一段
比如
tail -1 /etc/passwd | cut -d: -f6,7
则以:为分割,取第6段和第7段
LINES=`wc -l /etc/passwd | cut -d" " f1`
echo $LINES
行数统计,统计完成之后,对每一行进行切割,再向用户进行问好
=============================\
练习:
1, 设定变量FILE的值为/etc/passwd
2,向/etc/passwd 下的 用户问好,
3,统计一共有多少用户
#!/bin/bash
FILE='/etc/passwd'
LINES=`wc -l $FILE | cut -d" " -f1`
for I in `seq 1 $LINES`; do
USERNAME=`head -$I $FILE | tail -l | cut -d: -f1`
USERID=`head -$I $FILE | tail -l | cut -d: -f3`
echo "Hello, $USERNAME, Your UID is $USERID"
done
echo "$LINES users."
unset LINES FILE USERNAME USERID
练习2:
1, 设定变量FILE的值为/etc/passwd
2,向/etc/passwd 下的 用户问好,并说出对方ID是什么,形如:
Hello,root,your UID is 0
3,统计一共有多少用户
#!/bin/bash
#
FILE='/etc/passwd'
LINES=`wc -l $FILE | cut -d" " -f1`
for I in `seq 1 $LINES`; do
USERNAME=`head -$I $FILE | tail -1 | cut -d: -f1`
USERID=`head -$I $FILE | tail -1 | cut -d: -f3`
echo "Hello, $USERNAME,Your UID is $USERID"
done
echo "$LINES users."
unset LINES FILE USERNAME USERID
===================================
总结 for循环:
for VAR in LIST; do
statement;
statement;
...
done
LIST的表述形式:
简单列表:1 2 3 4
复杂列表: This is Tom\'s cat (此时里面的小分号不被LIST识别,需要转义符)
变量 `seq 1 $LINES`
命令 `ls /var`
通配符 for I in /var/*
-------------------------------
为了一次能从/etc/passwd下取一行,简单的可以使用:
for LINE in `cat /etc/passwd`; do
但是空格,TAB,换行符都被LIST识别为拆分
为了避免空格被拆分成一个独立的值去计算,需要在拆分前告诉LIST,
只需要修改$IFS的值为只识别换行值:
IFS=$'\n'
所以将刚才的练习2,重新可以编译成:
#!/bin/bash
#
IFS=$'\n' ##### 只识别换行符为拆分
let SUM=0 ##### 将SUM从数值型升为整形
for LINE in `cat /etc/passwd`; do ####将cat显示的结果依次输出循环
USERNAME=`echo $LINE | cut -d: -f1` ####取
USERID=`echo $LINE |cut -d: -f3`
echo "Hello, $USERNAME,your ID is $USERID."
SUM=$[$SUM+1]
done
echo "$SUM users."
--------------------------
SUM是数值型的,为了让他升值为整形,需要
let SUM=0
============================
grep 命令: 全面搜索正则表达式并显示出来,它是一个家族,家族中分别有
1.grep
2.egrep
3.fgrep
正则表达式(re):其实就是元字符,用一堆的元字符组成,按照一定的规则组成起来,能够实现过滤匹配不同的文本内容。
1.基本正则表达式
2.扩展正则表达式,比基本略多
-----------
格式
grep [options] "PATTERN" file
能够根据你所指的模式(PATTERN),从文件中将他找到并显示出来
比如: grep 'root' /etc/passwd
搜索/etc/passwd中的含有root的信息
常用选项
-i : 不在乎字符的大小写
-v:跟默认的动作正好相反(取反),只显示那些没有匹配的
-n:能够显示所匹配的行在文中所处得行号(行号显示)
-An:显示结果,并显示结果之后的n行
-Bn:显示结果,并显示结果之前的n行
-Cn:显示结果,并显示结果的前 和 后 n 行
--color:会把匹配这个模式的字符串高亮显示
常用模式 :正则表达式种类
^ 定义行首,行首匹配锚定符
$ 定义行尾,行尾匹配锚定符
. 匹配任意单个字符,相当于"?"
* 次数匹配,表示匹配符前面的这个字符所出现的0次或者任意次。
? 次数匹配,表示匹配符前面的这个字符所出现的0次或者1次。
[ ] 匹配一组字符中的任意一个字符
[x-y] 匹配指定范围内的一个字符
[^ ] 表示否定
\< 词首定位符,必须是单词,而不能是字符
\> 词尾定位符
\<..\> 精确匹配
\(..\) 撇配稍后将要使用的字符的标签
x\{m\} 表示x精确出现m次
x\{m,\} 表示x至少出现m次
x\{m,n\} 表示x至少出现m次,且不超过n次
grep --color "root{1}quot; grep.txt 表示root必须出现在行尾
grep --color "\<root\>" grep.txt 表示要精确匹配root这个单词
grep --color "root\{1,\}" grep.txt 至少匹配1次
grep --color "root\{1\}" grep.txt 表示精确匹配1次
grep --color "root\{0,1\}" grep.txt 表示至多出现 1次
grep --color "\(root\).*\1able" grep.txt
包含了root中间跟了任意字符,后又跟了\1又出现了引用最前面的字符的以及 able字符的字符
===练习============
练习:
1、显示/proc/meminfo文件中以不区分大小的s开头的行;
2、显示/etc/passwd中以nologin结尾的行;
3、显示/etc/inittab中以#开头,且后面跟一个或多个空白字符,而后又跟了任意字符的行;
4、显示/etc/inittab中包含了:一个数字:(即两个冒号中间一个数字)的行;
5、显示/boot/grub/grub.conf文件中以一个或多个空白字符开头的行;
6、显示/etc/inittab文件中以一个数字开头并以一个与开头数字相同的数字结尾的行;
7、ifconfig命令可以显示当前主机的IP地址相关的信息等,如果使用grep等文本处理命令取出本机的各IP地址,要求不包括127.0.0.1;
8、显示/etc/sysconfig/network-scripts/ifcfg-eth0文件中的包含了类似IP地址点分十进制数字格式的行;
1 grep -i --color "^s" /proc/meminfo
2 grep --color "nologin{1}quot; /etc/passwd
3 grep --color "^#[[:space:]]\{1,\}.*" /etc/inittab
4 grep --color ":[0-9]:" /etc/inittab
5 grep --color "^[[:space:]]\{1,\}.*" /boot/grub/grub.conf
6 grep --color "\([0-9]\).*\1{1}quot; /etc/inittab
7 ifconfig | grep "inet addr" | grep -v '127.0.0.1' | cut -d: -f2 | cut -d" " -f1
8 grep "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" /etc/sysconfig/network-scripts/ifcfg-eth0
===================
egrep,grep -E
与grep基本相同,但是没有了 \()之类的字符
{n} : 精确匹配前面的 n次
+ :匹配一个或多个+号前的字符,相当于 \{1,\}
? : 相当于\{0,1\},匹配此前的字符,1次或0次
a|b : 匹配a或b
( ) : 字符组,把括号里的当做一个单位
====================
练习,用egrep实现上面的练习。
1. egrep -i --color "^s" /proc/meminfo
2. egrep --color "nologin{1}quot; /etc/passwd
3. egrep --color "^#[[:space:]]+.*" /etc/inittab
4. egrep --color ":[0-9]:" /etc/inittab
5. egrep --color "^[[:space:]]+.*" /boot/grub/grub.conf
6.
7. ifconfig | egrep "inet addr" | egrep -v '127.0.0.1' | cut -d: -f2 | cut -d" " -f1
8. egrep "([0-9]{1,3}\.){3}[0-9]{1,3}" /etc/sysconfig/network-scripts/ifcfg-eth0
=====================
变量 $? : 表示上一个命令的执行结果 ,表示执行成功了还是失败了。
结果显示:
0 : 成功了
1-255 : 都表示失败了
事实上我们可以实现以逻辑连接命令来来连接两个命令
=======================
与操作
command && command: 这表示 只有前面的命令成功了,才执行后面的命令1 && 0 = 0 意思是前面的命令即使执行成功了,也需要执行后面的命令,才能判断是否成功
即: 短路操作、
只要前面为0,结果肯定为0
或操作
command || command : 如果前面不成功,则执行后面的
短路操作:
1 || ? = 1 :前面已经成功的时候,后面就不执行了,直接判定成功
只要前面为1,后面肯定为1
0 || ? = ?
若前面不成功,则必须执行后面的命令才行
grep "root" /etc/passwd || useradd root
判断是否需要执行这个用户,如果前面否定,则执行后面,意思是,若没有用户,则创建。
==================
练习:
写一个脚本:
1.添加10个用户,user1 --- user 10 ,但要求只有用户不存在的情况下才能添加
解答:
#!/bin/bash
#
for I in {1..10}; do
grep "user$I" /etc/passwd || useradd user$I
done
写一个脚本
1.通过 ping命令测试,192.168.0.151到192.168.0.254之间的所有主机是否在线
如果在线,则显示“ip is up” (ip 要换成真正的ip)
如果不在线,则显示“ip is down”
提示:ping -c1 -W1 192.168.0.254 &> /dev/null && echo "192.168.0.254 is up." || echo "192.168.0.254 is down."
ping
-Wn :如果超时,则过n秒后自动跳过
解答:
#!/bin/bash
#
for I in {151..254}; do
ping -c1 -W1 192.168.0.$I &> /dev/null && echo "192.168.0.$I is up." || echo "192.168.0.$I is down."
done