shell脚本学习4-输入和输出

两盒软妹~` 提交于 2019-12-27 05:11:16

上一节讲了shel的循环语句,本节介绍shell的输入和输出。输入包括命令行的输入和键盘或文件的输入,输出主要讲解输出重定向。

1. shell的输入

 

1.1 读取命令行中参数

        shell脚本通过美元符号读取命令行中的参数,比如$0表示程序名,$1表示第一个参数,$2表示第二个参数,${10}表示表示第10各参数,以此类推。建立脚本test34.sh如下:

#/bin/bash

total=$[ $1 * $2 + $3 ]

echo "$1 * $2 + $3 = $total"

运行如下:

./test34.sh 4 5 6

结果如下:

4 * 5 + 6 = 26

1)注意,当把字符串输入给shell脚本的时候,注意是以空格作为分隔符,如果字符串本身就有空格的话,那么用双引号或者单引号。举例脚本test35.sh如下:

#/bin/bash

echo "$1 is pretty nice!"

运行如下:

./test35.sh "wu lin wai zhuan"

结果如下:

wu lin wai zhuan is pretty nice!

2) 注意,$0是程序名,这个程序名是运行时输入的完整路径,而不是文件名。比如下面的test36.sh脚本:

#/bin/bash

echo "shell path is $0"

运行输入:

bash shell/test36.sh

结果如下:

shell path is shell/test36.sh

如果想要只截取出文件名的话可以使用basename命令,这个命令会返回不包含前面路径的脚本名。脚力脚本test37.sh

#/bin/bash

shellName=$(basename $0)

echo "shell path is $shellName"

运行输入:

bash shell/test37.sh

结果如下:
shell path is test37.sh

通过basename指令,我们可以通过脚本名来处理相应的逻辑,比如建立一个shell脚本,然后创建两个不同名字的链接,让这两个链接处理不同的逻辑。建立脚本test38.sh如下:

#/bin/bash

name=$(basename $0)

if [ $name = "add" ];then

    res=$[ $1 + $2 ]

elif [ $name = "mul" ];then

    res=$[ $1 * $2 ]

fi

echo "$0 -> result is $res"

然后,创建两个链接文件:

ln -s test38.sh add

ln -s test38.sh mul

输入:

./add 4 4

运行结果为:

./add -> result is 8

输入:

./mul 5 6

运行结果为:

./mul -> result is 30

 

1.2 常用的特殊参数

有时候需要知道命令行中所有参数的个数,可以使用$#;如果想知道最后一个命令行参数,使用${!#},如果没有参数的话,那么${!#}就是程序名;$*会把参数当做一个整体;$@会把参数也当做一个整体,不过可以用for指令一个个取出来,这样就可以遍历命令行参数。

建立脚本test39.sh如下:

#/bin/bash

echo "param num is $#"

echo "the last param is ${!#}"

for item in "$*";do

    echo "item -> $item"

done

for item in "$@";do

    echo "item -> $item"

done

运行如下:

./test39.sh wu lin wai zhuan

结果如下:

param num is 4

the last param is zhuan

item -> wu lin wai zhuan

item -> wu

item -> lin

item -> wai

item -> zhuan

 

1.3 使用shift移动参数

shfit指令会把命令行中的参数进行左移操作,把$3的值给$2,$2的值给$1,$1的值被丢弃。注意$1的值不会给$0。shfit后面可以加参数,表示左移几个参数,比如shift 2表示左移2

建立如下脚本:

#/bin/bash

while [ -n "$1" ];do

    echo "param is $1"

    shift

done

运行如下:

./test40.sh wu lin wai zhuan

结果如下:

param is wu

param is lin

param is wai

param is zhuan

 

1.2 读取用户的输入

除了在运行脚本的时候加入参数,还可以通过read命令来读取用户的输入。建立脚本如下:

#/bin/bash

echo "input the name"

read name

echo "name is $name"

运行脚本后并输入wu lin wai zhuan

结果如下:

name is wu lin wai zhuan

这个脚本用echo命令提示用户输入数据,并把输入读入了name这个变量。如果在read后面加上-p参数,也可以提示用户输入。建立脚本如下:

#/bin/bash

read -p "input the name:" name

echo "name is $name"

运行脚本后输入:wu lin wai zhuan

结果如下:

input the name:wu lin wai zhuan

name is wu lin wai zhuan

read命令会把输入进行划分,并把输入分配给单个变量,如果指定的变量个数不够的话,那么剩下的输入全部扔给最后一个变量。比如:

#/bin/bash

read -p "input name and age and other: " name age other

echo "name=$name,age=$age,other=$other"

运行脚本后输入如下数据并得到下面的结果:

input name and age and other: baizhantang 30 mo xiao bei

name=baizhantang,age=30,other=mo xiao bei

注意:如果不指定输入变量的话,输入的所有数据都会保留到REPLY这个环境变量中,比如
#/bin/bash

read -p "input name: "

echo "name is $REPLY"
运行脚本后输入数据得到结果如下:

input name: wu lin wai zhuan

name is wu lin wai zhuan

read命令可以指定超时时间,也就是说,用户如果在规定时间内没有输入的话,read命令就会产生一个非零返回值,比如:
#/bin/bash

if read -t 3 -p "input name: " name

then

    echo "name is $name"

else

    echo "input timeout"

fi

运行后不输入数据,结果为:

input name: input timeout

read命令可以使用-n参数指定用户输入的字符个数,当用户输入个数满足要求时会自动退出。比如

#/bin/bash

read -n1 -p "input answer:" answer

case $answer in

Y | y)

    echo "answer is yes";;

N | n)

    echo "answer is no"

    exit;;

esac

运行脚本后,输入yes,,不过当输入y之后脚本立刻就往下执行了

input answer:yanswer is yes

 

之前的read命令会把用户的输入都显示出来,但是有些数据不希望在屏幕上显示的时候,可以增加-s参数,比如:

#/bin/bash

read -s -p "input password " password

echo "password is $password

运行后输入:123

结果为:

input password password is 123

1.3 从文件中读取

read命令可以从文件中而非用户从键盘中输入的数据,read命令每次从文件读取一行,读完之后会返回一个非0的返回值。比如,有一个文件testfiel,testfile的内容为:
au linwaizhuan

Aulinw ai zhuan

建立脚本如下:
#/bin/bash

cat testfile|while read line

do

    echo "content is : $line"

done

运行后结果为:
content is : au linwaizhuan

content is : Aulinw ai zhuan

2. shell的输出

之前接触的shell输出是把内容打印到屏幕或者重定向到文件中,这次对其原理介绍一下。

 

2.1 标准文件描述符

linux系统中一切皆文件,通过文件描述符来表示一个文件。之前经常接触的有3个,分别是标准输入0,标准输出1,标准错误2。

2.1.1 标准输入

标准输入就是键盘的输入,不过也可以使用重定向用指定的文件来代替文件描述符。比如使用重定向输入到cat命令和sort命令
cat < testfile
sort < testfile

2.1.2 标准输出

标准输出就是输出到显示器,不过也可以通过重定向输出到文件,比如

date > testfile

date >> testfile

这两个都是重定向,在testfile文件中能看到两行date输出的结果。但是,如果命令返回码($?非零),这个命令的输出结果就不能重定向到相应的文件中,比如

abcdef >testfile

结果为:

abcf:未找到命令

并且testfile文件中为空,并没有“abcf:未找到命令”这句话,看着像是重定向到文件的操作失败了,这里需要了解标准错误。

2.1.3 标准错误

默认情况下,标准错误是输出到屏幕上的。如果想要把错误也重定位到文件中,可以在重定位符号前面加上2这个数字,比如:

acbdef 2> testfile

运行后屏幕上什么都不显示,testfile中内容为:

未找到 'abcdef' 命令,您要输入的是否是:

命令 'abcde' 来自于包 'abcde' (universe)

abcdef:未找到命令

箭头前面的2意思是把标准错误重定向到testfile中。

 

注意:如果夹杂着正确的命令和错误的命令,那么就需要把标准输出和标准错误都进行重定向,比如:

ll testfile abed 1> file1 2> file2

运行后,file1的内容为:

-rw-rw-rw- 1 aitian aitian 78 12月 22 22:54 testfile

file2的内容为:

ls: 无法访问'abed': 没有那个文件或目录

 

当然,如果想要把标准输出和标准错误都输出到同一个文件中,可以使用&> 进行重定向,比如

ll testfile abc &> file3

运行后,file3的内容为:

ls: 无法访问'abc': 没有那个文件或目录

-rw-rw-rw- 1 aitian aitian 78 12月 22 22:54 testfile

可以发现,错误日志被优先放到了前面。

2.2 shell中如何输出重定向

在脚本中有两个方法实现输出重定向,分别是临时重定向脚本中某一行输出,永久重定向脚本中的所有输出。

2.2.1 临时重定向

在脚本中如果想把某行输出重定向到对应的文件描述符,只要在重定向符号和文件描述符之间增加&即可,比如说标准错误,使用>&2。举例如下:

#/bin/bash

date >&2

echo "date is $(date)"

当不加重定位文件运行时,即:

./test49.sh

结果为:

2019年 12月 23日 星期一 22:55:34 CST

date is 2019年 12月 23日 星期一 22:55:34 CST

若以重定向运行,即./test49.sh >file

屏幕上显示:

2019年 12月 23日 星期一 22:56:18 CST

file的内容为:

date is 2019年 12月 23日 星期一 22:56:18 CST

2.2.1 永久重定向

临时重定向只能对加了&符号的语句生效,可以如果想要把脚本中的多个输出都重定向的话就需要每一个输出都加上,这比较麻烦。但是通过永久重定向可以解决这个问题。永久重定向通过exec命令。比如

#/bin/bash

exec 1>testfile

date

echo "wu lin wai zhuan"

date

运行后,打开testfile,内容为:

2019年 12月 24日 星期二 22:52:13 CST

wu lin wai zhuan

2019年 12月 24日 星期二 22:52:13 CST

上面的exec命令是把标准输出重定向到testfile中,并且对整个shell的输出生效。

注意上面的exec是启动了一个新进程并完成重定向。

有时候一个shell脚本中,想要向不同的文件重定向,或者不同的fd重定向到不同的文件,也可以使用exec做到。比如

#/bin/bash

exec 2>errfile

echo "normal line 1"

echo "normal line 2"

exec 1>normalfile

echo "normal line 3"

echo "error line 2"

运行后,屏幕输出:

normal line 1

normal line 2

normalfile内容为:

normal line 3

errfile内容为:

error line 2

2.3 shell中如何输入重定向

用exec同样可以实现输入重定向,让shell脚本从文件中去读取消息而不是从键盘输入。比如:

#/bin/bash

exec 0< testfile

while read line;do

    echo "$(date) line content: $line"

done

运行后输出:

2019年 12月 25日 星期三 20:53:21 CST line content: 2019年 12月 24日 星期二 22:59:03 CST

2019年 12月 25日 星期三 20:53:21 CST line content: wu lin wai zhuan

2019年 12月 25日 星期三 20:53:21 CST line content: 2019年 12月 24日 星期二 22:59:03 CST

2.4 关闭文件描述符

#/bin/bash

exec 5> testfile

echo "fd 5 content" >&5

exec 5>&-

echo "fd 5 value" >&5

运行后,屏幕输出:

./test53.sh: 行 5: 5: 错误的文件描述符

testfile的内容为:

fd 5 content

3. lsof指令查看文件描述符相关操作

lsof命令能够查询文件描述符的相关信息,一般lsof -a后面加上-p或者-d的参数,-p表示指定进程的pid,后者表示指定文件描述符的编号。比如输入:

lsof -a -p $$ -d 2

输出为:

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

bash    8221 at    2u  CHR  136,0      0t0    3 /dev/pts/0

其中,

COMMAND表示正在运行的可执行文件的名字的前9个字符;

PID为对应进程的pid

USER表示用户

FD表示描述符和访问类型,r表示读,w表示写,u表示读写

TYPE表示文件的类型,CHR代表字符型(标准输入,输出,错误都是这种类型),BLK表示块型,DIR表示目录,REG表示常规文件,REG最常见

DEVICE表示设备的设备号

SIZE表示文件的大小

NODE表示文件的节点号

NAME表示文件名

 

现在建立一个脚本:

#/bin/bash

exec 23> test23

exec 34< testfile

exec 5> test5

lsof -a -p $$ -d 0,1,2,23,34,5

运行后结果为:

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

bash    12205 aitian    0u  CHR  136,0      0t0      3 /dev/pts/0

bash    12205 aitian    1u  CHR  136,0      0t0      3 /dev/pts/0

bash    12205 aitian    2u  CHR  136,0      0t0      3 /dev/pts/0

bash    12205 aitian    5w  REG  8,21        0 538178 /home/aitian/at/shell/test5

bash    12205 aitian  23w  REG  8,21        0 538174 /home/aitian/at/shell/test23

bash    12205 aitian  34r  REG  8,21      13 537315 /home/aitian/at/shell/testfile

 

4. 特殊的/dev/null文件

任何内容输出到/dev/null文件都不会被保存,相当于它是一个自动清空的回收站。有些数据如果不想保存的话可以往这里面重定向。

比如:

date > /dev/null

运行后什么都不会显示。

再比如:

ll testfile abcde 2>/dev/null

输出:

-rw-rw-rw- 1 aitian aitian 13 12月 25 21:25 testfile

那段错误信息被放进了回收站了。

以后如果想要清空一个文件的话,可以输入:

cat /dev/null > testfile

 

5.  tee命令实现既输出到屏幕又输出到文件

如果想要把输出的内容既显示到屏幕又要重定向到文件中,可以使用tee命令。比如

date | tee testfile

输出:

2019年 12月 25日 星期三 22:18:19 CST

并且testfile的内容也是这个。

tee命令默认是覆盖,如果想要追加的话,使用-a 选项。比如

date | tee testfile

输出:

2019年 12月 25日 星期三 22:23:14 CST

此时testfile的内容为:

2019年 12月 25日 星期三 22:18:19 CST

2019年 12月 25日 星期三 22:23:14 CST

6. 总结与展望

6.1总结

1)shel如何使用命令行的参数,比如$1为参数1,$#为参数个数,$*和$@表示所有的参数,但是$@可以用for循环一个个取出来;

2)  用shift命令左移参数,一次移动一个,shift n一次移动n个;

3)  read命令读取用户的输入, read -p读取时候加提示语句,read -t3定时3s,read -n1读取到一个字符的时候马上退出;

4)   标准输出,标准输入,标准错误是个啥;

5)临时重定向:比如用>&2把标准输出临时重定向

6)永久重定向:比如用exec 1>testfile命令;

7) 输入重定向和关闭文件描述符

8) lsof命令怎么用,常常加-a,-p,-d参数

9)/dev/null这个黑洞文件

10)tee命令输出屏幕和文件

6.2 展望

下次更新将学习使用shell脚本运行时的一些控制。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!