首页 > 代码库 > 3、输入输出重定向、bash算术、正则表达式 学习笔记

3、输入输出重定向、bash算术、正则表达式 学习笔记

输入输出重定向和管道

INPUT: 标准输入 stdin

OUPUT: 标准输出 stdout 1

标准错误 stderr 2


I/O重定向

输入重定向:<, <<

<: 输入重定向

<< EOF: 此处创建文件, Here Document

常用于在脚本中创建文件或生成菜单;

显示如下菜单

c:show cpu info

d:show disk inf

m:show men info


脚本实现

#!/bin/bash

cat << EOF

c:show cpu info

d:show disk info

m:show men info

EOF


输出重定向:>, >>

>: 覆盖输出

>>: 追加输出

set -C:禁止使用覆盖重定向至已经存在的文件;

set +C: 关闭上述特性;

>|:在-C特性下,强制使用覆盖重定向;


/dev/null: bit bucket,位桶


错误重定向:2>, 2>>

2>: 覆盖

2>>: 追加


同时重定标准输出和错误输出:

COMMAND > /path/to/outfile 2> /path/to/errfile

COMMAND &> /path/to/somefile

COMMAND > /path/to/somefile 2>&1  


管道:

COMMAND1 | COMMAND2 | COMMAND3 | ...       

bash中的算术运算

declare

-i: 整型变量

-x: 环境变量, 类似于export


let varName=算术表达式

varName=$[算术表达式]

varName=$((算术表达式))

varName=`expr $num1 + $num2`


如果计算结果中存在小数,将会被圆整:


操作符:+, -, *, /, %

+=, -=, *=, /=, %=


练习:计算100以内所有正整数之和;

#!/bin/bash

#

declare -i sum=0

for i in {1..100}; do

  let sum=$sum+$i

done


echo "The sum is: $sum."


知识点:bash的单步执行:

bash -x /path/to/script


练习:分别计算100以内所有偶数之和和奇数之和;

#!/bin/bash

declare -i oddSum=0,evenSum=0


for i in `seq 1 2 100`; do

  oddSum=$[$oddSum+$i]

done


for j in `seq 2 2 100`; do

evenSum=$[$evenSum+$j]

done


echo "The Even Sum is: $evenSum, the odd sum is: $oddSum"


练习:计算当前系统所有用户的ID之和;


#!/bin/bash

declare -i uidSum=0

for i in `cut -d: -f3 /etc/passwd`; do

uidSum=$[$uidSum+$i]

done


echo "The UIDSum is: $uidSum."


练习:计算/etc/rc.d/rc.sysinit、/etc/init.d/functions和/etc/issue三个文件的字符数之和;

#!/bin/bash

#

declare -i bytesCount=0

for file in /etc/rc.d/rc.sysinit /etc/init.d/functions /etc/issue; do

let bytesCount=$bytesCount+`wc -c $file | cut -d‘ ‘ -f1`

done


echo $bytesCount


练习:新建用户tmpuser1-tmpuser10,并计算他们的id之和;

#!/bin/bash

#

declare -i uidSum=0

for i in {1..10}; do

  useradd tmpuser$i 

  let uidSum=$uidSum+`id -u tmpuser$i`

done


知识点:位置参数

位置参数:

/tmp/test.sh 3 89 

$0: 脚本自身

$1: 脚本的第一个参数

$2

...


特殊变量:

$#: 位置参数的个数;

$*,$@: 引用所有的位置参数;


知识点:交互式脚本

read

-t 指定输入超时时间

-p 可以输入提示语


知识点:给变量以默认值

varName=${varName:-value}

如果varName不空,则返回varName的值;否则,则返回value;

如果varName不空,则其值不变;否则,varName会使用value作为其值;


练习:通过键盘给定一个目录路径,默认为/,来判断目录下文件内容的类型;


#!/bin/bash


read -t 5 -p "Please input a dirPath:" dirPath

dirPath=${dirPath:-"/"}

echo "The dirPath what you input is: $dirPath"


for filename in "$dirPath/*"

do

        file $filename

done



文本处理工具之grep、egrep和fgrep:


grep: (global search regular expression(RE) and print out the line


文本搜索工具,根据用户指定的文本模式对目标文件进行逐行搜索,显示能够被模式所匹配到的行


格式:grep [options] ‘PATTERN‘ file,...


grep 常用选项:

-v 反向,显示不能被模式所匹配到的行;

-o 仅显示被模式匹配到的字串,而非整行;

-i 不区分字符大小写, ignore-case

-E 支持扩展的正则表达式

-A # 显示匹配行后面的#行

-B # 显示匹配行前面的#行

-C # 显示匹配行前后的#行

--color=auto                         颜色显示匹配到的字符


正则表达式:是一类字符所书写出的模式(pattern)

元字符:不表示字符本身的意义,用于额外功能性的描述


基本正则表式的元字符:grep -E

字符匹配:

. 匹配任意单个字符

[] 匹配指定范围内的任意单个字符

[^] 匹配指定范围外的任意单个字符

[0-9], [[:digit:]]                 匹配数字0-9

[a-z], [[:lower:]]                 匹配小写字母a-z

[A-Z], [[:upper:]]                 匹配大写字母A-Z

[[:alpha:]] 匹配所有字母a-zA-Z

[[:alnum:]] 匹配所有字母数字0-9a-zA-Z

[[:space:]] 匹配所有空白字符

[[:punct:]] 匹配所有标点符号


次数匹配:用来指定匹配其前面的字符的次数

* 匹配任意次,可以是0次或多次

.* 匹配任意长度的任意字符

\? 匹配0次或1次

\{m\} 匹配m次

\{m,n\} 最少匹配m次,最多匹配n次

\{m,\} 最少匹配m次

\{0,n\} 最多匹配n次;

位置锚定:用于指定字符出现的位置

^ 锚定行首(^char)

$ 锚定行尾(char$)

^$ 空白行

\< 锚定词首也可以用(\<char或\bchar)

\> 锚定词尾也可以用(char\>char\b)

分组:

\(\)

引用:

\1 后向引用,引用前面的第一个左括号以及与之对应的右括号中的模式所匹配到的内容

\2

...


He like his lover.

She love her liker.

He love his lover.

She like her liker.


grep "\(l..e\).*\1" test.txt 

    He love his lover.

    She like her liker.


练习:

1、显示/proc/meminfo文件中以大小写s开头的行;

# grep "^[sS]" /proc/meminfo

# grep -i "^s" /proc/meminfo

2、取出默认shell为非bash的用户;

# grep -v "bash$" /etc/passwd | cut -d: -f1

3、取出默认shell为bash的且其ID号最大的用户;

# grep "bash$" /etc/passwd | sort -n -t: -k3 | tail -1 | cut -d: -f1

4、显示/etc/rc.d/rc.sysinit文件中,以#开头,后面跟至少一个空白字符,而后又有至少一个非空白字符的行;

# grep "^#[[:space:]]\{1,\}[^[:space:]]\{1,\}" /etc/rc.d/rc.sysinit

5、显示/boot/grub/grub.conf中以至少一个空白字符开头的行;

# grep "^[[:space:]]\{1,\}[^[:space:]]\{1,\}" /boot/grub/grub.conf 

6、找出/etc/passwd文件中一位数或两位数;

# grep --color=auto "\<[0-9]\{1,2\}\>" /etc/passwd

7、找出ifconfig命令结果中的1到255之间的整数;

# ifconfig | grep -E --color=auto "\<([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"

8、查看当前系统上root用户的所有信息;

# grep "^root\>" /etc/passwd

9、添加用户bash和testbash、basher,而后找出当前系统上其用户名和默认shell相同的用户;

# grep --color=auto "^\([[:alnum:]]\{1,\}\)\>.*\1$" /etc/passwd

10、找出netstat -tan命令执行的结果中以“LISTEN”或“ESTABLISHED”结尾的行;

# netstat -tan | egrep "(LISTEN|ESTABLISHED)[[:space:]]+"

11、取出当前系统上所有用户的shell,要求:每种shell只显示一次,且按升序显示;

# cut -d: -f7 /etc/passwd | sort -u

挑战题:写一个模式,能匹配真正意义上的IP地址;(1.0.0.1--223.255.255.254)



egrep: 使用扩展正则表达来构建模式,相当于grep -E 

字符匹配:

. 匹配任意单个字符               

[] 匹配指定范围内的任意单个字符   

[^] 匹配指定范围外的任意单个字符   

次数匹配: 

* 匹配任意次,可以是0次或多次                        .* 匹配任意长度的任意字符                             ? 匹配0次或1次

        + 匹配其前面的字符至少1次                            {m} 匹配m次                                          {m,n} 最少匹配m次,最多匹配n次                         {m,} 最少匹配m次                                      {0,n} 最多匹配n次;                                    

锚定:

^ 锚定行首(^char)               

$ 锚定行尾(char$)               

^$ 空白行                          

\< 锚定词首也可以用(\<char或\bchar

\> 锚定词尾也可以用(char\>char\b)

分组:

() 分组

|        conC|cat的结果为conC或cat;con(C|c)at的结果为conCat或concat


引用:                                                                                   

\1 向引用,引用前面的第一个左括号以及与之对应的右括号中的模式所匹配到的内容        \2                                                                                      ...                                                                                    

练习:写一个脚本,分别统计/etc/rc.d/rc.sysinit、/etc/init.d/functions和/etc/fstab文件中各自以#开头的行的行数,以及空白行的行数;

#!/bin/bash


for fileName in /etc/rc.d/rc.sysinit /etc/init.d/functions /etc/fstab

do

        line1=`egrep "^#" $fileName | wc -l`

        line2=`egrep "^$" $fileName | wc -l`

        echo "$fileName #:$line1 space:$line2"

done



练习:写一个脚本,分别复制/etc/rc.d/rc.sysinit、/etc/init.d/functions和/etc/fstab文件至/tmp目录中,文件名为原名后跟上当前的日期组成;

例如第一个文件复制后的名称为/tmp/rc.sysinit-2014-02-16;


#!/bin/bash


for fileName in /etc/rc.d/rc.sysinit /etc/init.d/functions /etc/fstab

do

        cp $fileName /tmp/`basename $fileName`-`date "+%F"`

done

      


练习:写一个脚本

显示当前系统上所有默认shell为bash的用户的用户名、UID以及其在/etc/passwd文件中的行号;


#!/bin/bash

for userName in `grep "bash$" /etc/passwd | cut -d: -f1`

do

        lineNum=`cat -n /etc/passwd | egrep "[[:space:]]+$userName" | egrep -o "[0-9]+[[:space:]]+"`

        userId=`egrep "^$userName" /etc/passwd | cut -d: -f3`

        echo "$lineNum $userName $userId"

done


bash编程之条件判断:判定后续操作的前提条件是否满足。


条件判断的常用测试类型:

整数测试

字符测试

文件测试


bash中如何做测试:

test EXPRESSION

[ EXPRESSION ]

[[ EXPRESSION ]]


bash中条件判断使用if:

单分支:

if 条件; then

分支1; 

fi


双分支:

if 条件; then

分支1;

else

分支2;

fi


多分支:

if 条件1; then

分支1;

elif 条件2; then

分支2;

elif 条件3; then

分支3;

...

else

分支n;

fi


如果文件有空白行,就显示空白行数;否则,就说明文件无空白行;

#!/bin/bash

#

read -p "Enter a file path: " fileName


if grep "^$" $fileName &> /dev/null; then

        linesCount=`grep "^$" $fileName | wc -l`

        echo "$fileName has $linesCount space lines."

else

    echo "$fileName hava no space line."

fi



bash编程之:整数测试

二元测试:

num1 OPRAND num2

-gt: 大于[ $num1 -gt $num2 ]

-lt: 小于

-ge:大于等于

-le: 小于等于

-ne: 不等于

-eq: 等于


bash知识点之脚本自定义退出:

exit [n]


练习:判定两个数孰大孰小,整数是通过命令行参数传递而来。


#!/bin/bash

#

if [ $# -lt 2 ]; then

        echo "Stupid..."

        echo "Usage: `basename $0` argu1 argu2"

        exit 4

fi


if [ $1 -gt $2 ]; then

        echo "The max num is $1."

else

    echo "The max num is $2."

fi


bash知识点:只要命令用作条件,就表示引用是其状态结果(即执行成功与否),而非命令的输出结果,因此,不能使用命令替换符;


grep "^root\>" /etc/passwd

id root


练习:写一脚本,实现如下功能:

1、让用户通过键盘输入一个用户名

2、如果用户存在,就显示其用户名和UID;

3、否则,就显示用户不存在;


#!/bin/bash

read -t 10 -p "Enter a username: " userName

# userName=${userName:-root}

if id $userName &> /dev/null; then

userID=`id -u $userName`

echo "$userName: $userID"

else

echo "$userName not exist."

fi



练习:写一脚本,实现如下功能:

1、让用户通过键盘输入一个用户名,如果用户不存在就退出;

2、如果用户的UID大于等于500,就说明它是普通用户;

3、否则,就说明这是管理员或系统用户;


#!/bin/bash



# exit 6 -- 

read -t 10 -p "Enter a username: " userName

if ! id $userName &> /dev/null; then

echo "$userName not exist."

exit 6

fi


userID=`id -u $userName`


if [ $userID -ge 500 ]; then

echo "A common user."

else

echo "Admin or System user."

fi



bash的知识点:

组合条件测试:对条件做逻辑运算

与:条件1 && 条件2

条件1为假,则最终结果一定为假,因此,条件2将不执行;

条件1为真,则最终结果决于后面条件,因此,条件2必须执行; 

或:条件1 || 条件2

条件1为真,则最终结果一定为真,因此,条件2将不再执行;

条件1为假,则最终结果决于后面条件,因此,条件2必须执行; 

非: ! 条件



练习:写一脚本,实现如下功能:

1、让用户通过键盘输入一个用户名,如果用户不存在就退出;

2、如果其UID等于其GID,就说它是个"good guy"

3、否则,就说它是个“bad guy”;


#!/bin/bash

# exit 6 -- 

read -t 10 -p "Enter a username: " userName

if ! id $userName &> /dev/null; then

echo "$userName not exist."

exit 6

fi


if [ `id -u $userName` -eq `id -g $userName` ]; then

echo "Good guy."

else

echo "Bad guy."

fi


扩展:判断当前系统上的所有用户是Good guy还是Bad guy.


for userName in `cut -d: -f1 /etc/passwd`; do

done



练习:写一个脚本,实现如下功能:

1、添加10个用户stu1-stu10;但要先判断用户是否存在;

2、如果存在,就用红色显示其已经存在

3、否则,就添加此用户;并绿色显示;

4、最后显示一共添加了几个用户;


#!/bin/bash

#

declare -i userCount=0

for i in {1..10}; do

if id stu$i &> /dev/null; then

echo -e "\033[31mstu$i\033[0m exists."

else

useradd stu$i && echo -e "add \033[32mstu$i\033[0m finished."

let userCount++

fi

done


echo "Add $userCount users."



练习:求200以内所有3的整数倍的正整数的和;

#!/bin/bash

declare -i sum=0

for i in {1..200}; do

if [ $[$i%3] -eq 0 ]; then

let sum+=$i

fi

done


echo "The sum is: $sum."


本文出自 “开源殿堂” 博客,请务必保留此出处http://kaiyuandiantang.blog.51cto.com/10699754/1936647

3、输入输出重定向、bash算术、正则表达式 学习笔记