shell基础中

1 处理用户输入

1.1传递参数

向 shell 脚本传递数据的最基本方法是使用命令行参数。命令行参数允许运行脚本时在命令行中添加数据: 
./addem 10 30 

将文本字符串作为参数传递时,引号并不是数据的一部分,仅用于表明数据的起止位置。
./addem "a b"

1.2 读取参数 

bash shell 会将所有的命令行参数都指派给称作位置参数(positional parameter)的特殊变量。
这也包括shell脚本名称。位置变量的名称都是标准数字:$0对应脚本名,$1对应第一个命令行参数,$2对应第二个命令行参数,以此类推,直到$9。

1.3 读取脚本名

可以使用位置变量$0获取在命令行中运行的shell脚本名。

#!/bin/bash 
echo This script name is $0. 
exit

这里有一个潜在的问题。如果使用另一个命令来运行 shell脚本,则命令名会和脚本名混
在一起,出现在位置变量$0中: 
./positional0.sh 

This script name is ./positional0.sh. 

如果运行脚本时使用的是绝对路径,那么位置变量$0 就会包含整个路径

$HOME/scripts/positional0.sh 

This script name is /home/christine/scripts/positional0.sh. 

basename命令可以返回不包含路径的脚本名

name=$(basename $0)

2 特殊参数变量 

2.1 参数统计 


特殊变量$#含有脚本运行时携带的命令行参数的个数
变量${!#}获取最后一个位置参数

当命令行中没有任何参数时,$#的值即为0,但${!#}会返回命令行中的脚本名

2.2 获取所有的数据

$*变量和$@变量可以轻松访问所有参数,它们各自包含了所有的命令行参数。

$*变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。
基本上,$*变量会将这些参数视为一个整体,而不是一系列个体。 

$@变量会将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数。这通常使用for命令完成。

#!/bin/bash 
echo "Using the \$* method: $*" 
count=1 
for param in "$*" 
do 
     echo "\$* Parameter #$count = $param" 
     count=$[ $count + 1 ] 
done 

echo 
echo "Using the \$@ method: $@" 
count=1 
for param in "$@" 
do 
     echo "\$@ Parameter #$count = $param" 
     count=$[ $count + 1 ] 
done 
echo 
exit 

3 移动参数

bash shell 工具箱中的另一件工具是 shift 命令,该命令可用于操作命令行参数。跟字面上的意思一样,shift命令会根据命令行参数的相对位置进行移动。
在使用 shift 命令时,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量$3的值会移入s2,变量s2的值会移入s1,而变量s1的值则会被删除(注意,变量s0的值也就是脚本名,不会改变)。
这是遍历命令行参数的另一种好方法,尤其是在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量

#!/bin/bash#

echo "Using the shift method:"
count=1
while [ -n "$1" ] 
do
echo "Parameter #$count=$1"
count=$[ $count +1 ]
shift
done
echo
exit

使用shift命令时要小心。如果某个参数被移出,那么它的值就被丢弃了,无法再恢复。 

另外,也可以一次性移动多个位置。只需给shift 命令提供一个参数,指明要移动的位置数即可

#!/bin/bash 
echo "The original parameters: $*" 
echo "Now shifting 2..." 
shift 2 
echo "Here's the new first parameter: $1" 
exit 


4 处理选项

4.1 处理简单选项

在提取单个参数时,使用case语句来判断某个参数是否为选项

#!/bin/bash 
echo 
while [ -n "$1" ] 
do 
     case "$1" in 
          -a) echo "Found the -a option" ;; 
          -b) echo "Found the -b option" ;; 
          -c) echo "Found the -c option" ;; 
          *) echo "$1 is not an option" ;; 
     esac 
     shift 
done 
echo 
exit

./extractoptions.sh -a -b -c -d

case 语句会检查每个参数,确认是否为有效的选项。找到一个,处理一个。 
无论选项在命令行中以何种顺序出现,这种方法都能应对

4.2 分离参数和选项

在Linux中,这个特殊字符是双连字符(--)。shell会用双连字符表明选项部分结束。在双连字符之后,脚本就可以放心地将剩下的部分作为参数处理了

要检查双连字符,只需在case语句中加一项即可

#!/bin/bash 
echo 
while [ -n "$1" ] 
do 
     case "$1" in 
          -a) echo "Found the -a option" ;; 
          -b) echo "Found the -b option" ;; 
          -c) echo "Found the -c option" ;; 
          --) shift 
              break;; 
          *) echo "$1 is not an option" ;; 
     esac 
     shift 
done 

echo 
count=1 
for param in $@ 
do 
     echo "Parameter #$count: $param" 
     count=$[ $count + 1 ] 
done 
echo 
exit


4.3 处理含值的选项 

#!/bin/bash 
# Extract command-line options and values 

echo 
while [ -n "$1" ] 
do 
     case "$1" in 
          -a) echo "Found the -a option" ;; 
          -b) param=$2 
              echo "Found the -b option with parameter value $param" 
              shift;; 
          -c) echo "Found the -c option" ;; 
          --) shift 
              break;; 
          *) echo "$1 is not an option" ;; 
     esac 
     shift 
done 

echo 
count=1 
for param in $@ 
do 
     echo "Parameter #$count: $param" 
     count=$[ $count + 1 ] 
done 
exit

在这个例子中,case语句定义了3个要处理的选项。-b选项还需要一个额外的参数值。由于要处理的选项位于$1,因此额外的参数值就应该位于$2(因为所有的参数在处理完之后都会
被移出)。只要将参数值从$2变量中提取出来就可以了。当然,因为这个选项占用了两个位置,所以还需要使用shift命令多移动一次。


4.4 合并多个选项

getopt

#!/bin/bash 
# Extract command-line options and values with getopt 

set -- $(getopt -q ab:cd "$@") 

echo 
while [ -n "$1" ] 
do 
     case "$1" in 
          -a) echo "Found the -a option" ;; 
          -b) param=$2 
              echo "Found the -b option with parameter value $param" 
              shift;; 
          -c) echo "Found the -c option" ;; 
          --) shift 
              break;; 
          *) echo "$1 is not an option" ;; 
     esac 
     shift 
done 

echo 
count=1 
for param in $@ 
do 
     echo "Parameter #$count: $param" 
     count=$[ $count + 1 ] 
done 
exit

getopt 命令并不擅长处理带空格和引号的参数值。

getopts

#!/bin/bash 
# Extract command-line options and values with getopts 

echo 
while getopts :ab:c opt 
do 
     case "$opt" in 
          a) echo "Found the -a option" ;; 
          b) echo "Found the -b option with parameter value $OPTARG";; 
          c) echo "Found the -c option" ;; 
          *) echo "Unknown option: $opt" ;; 
     esac 
done 
exit 


5 获取用户输入

5.1 基本的读取

read 命令从标准输入(键盘)或另一个文件描述符中接受输人。获取输入后,read 命令会将数据存人变量。

#!/bin/bash

echo -n"Enter your name:
read name
echo "Hello Sname,welcome tomy script."
exit

非常简单。注意,用于生成提示的echo命令使用了-n选项。该选项不会在字符串末尾输出换行符,允许脚本用户紧跟其后输入数据。这让脚本看起来更像表单。

实际上,read命令也提供了-p选项,允许直接指定提示符

#!/bin/bash 
read -p "Please enter your age: " age 
days=$[ $age * 365 ] 
echo "That means you are over $days days old!" 
exit 

也可以在read命令中不指定任何变量,这样read命令便会将接收到的所有数据都放进特殊环境变量REPLY中

#!/bin/bash 
read -p "Enter your name: " 
echo 
echo "Hello $REPLY, welcome to my script." 
exit 

5.2 超时

用-t选项来指定一个计时器。-t选项会指定read命令等待输入的秒数。如果计时器超时,则read命令会返回非0退出状态码

#!/bin/bash 
if read -t 5 -p "Enter your name: " name 
then 
     echo "Hello $name, welcome to my script." 
else 
fi 
exit 


5.3 无显示读取 

-s 选项可以避免在read 命令中输入的数据出现在屏幕上(其实数据还是会被显示,只不过read命令将文本颜色设成了跟背景色一样)

#!/bin/bash  
read -s -p "Enter your password: " pass 
echo 
echo "Your password is $pass" 
exit 


5.4 从文件中读取 

#!/bin/bash 
count=1 
cat $HOME/scripts/test.txt | while read line 
do 
     echo "Line $count: $line" 
     count=$[ $count + 1 ] 
done 
echo "Finished processing the file." 
exit

6 呈现数据

6.1 理解输入和输出

Linux 系统会将每个对象当作文件来处理,这包括输入和输出。Linux 用文件描述符来标识每个文件对象。文件描述符是一个非负整数,唯一会标识的是会话中打开的文件。
每个进程一次最多可以打开9个①文件描述符。出于特殊目的,bash shell保留了前3个文件描述符(0、1和2)

文件描述符  缩  写   描  述 
0             STDIN    标准输入
1           STDOUT   标准输出
2           STDERR   标准错误

6.2 只重定向错误

ls -al badfile 2> test4

6.3  重定向错误消息和正常输出

ls -al test test2 test3 badtest 2> test6 1> test7

6.4 特殊的重定向

将STDERR和STDOUT的输出重定向到同一个文件。为此,bash shell提供了特殊的重定向符&>

ls -al test test2 test3 badtest &> test7

等价于

ls -al test test2 test3 > test7 2>&1

将错误输出重定向到标准输出


6.5 临时重定向


#!/bin/bash  
echo "This is an error" >&2 
echo "This is normal output" 

./test8 2> test9


6.6 永久重定向

如果脚本中有大量数据需要重定向,那么逐条重定向所有的echo语句会很烦琐。这时可以用exec命令,它会告诉shell在脚本执行期间重定向某个特定文件描述符

#!/bin/bash 
# redirecting all output to a file 
exec 1>testout 
echo "This is a test of redirecting all output" 
echo "from a script to another file." 
echo "without having to redirect every individual line" 

exec 命令会启动一个新 shell 并将 STDOUT 文件描述符重定向到指定文件。脚本中送往STDOUT 的所有输出都会被重定向。

6.7 创建自己的重定向

在脚本中重定向输入和输出时,并不局限于这3个默认的文件描述符。前文提到过,在shell
中最多可以打开9个文件描述符。替代性文件描述符从3到8共6个,均可用作输入或输出重定向。
这些文件描述符中的任意一个都可以分配给文件并用在脚本中。
 
创建输出文件描述符

可以用exec命令分配用于输出的文件描述符。和标准的文件描述符一样,一旦将替代性文件描述符指向文件,此重定向就会一直有效,直至重新分配。

#!/bin/bash 
# using an alternative file descriptor 
exec 3>test13out 
echo "This should display on the monitor" 
echo "and this should be stored in the file" >&3 
echo "Then this should be back on the monitor" 

也可以不创建新文件,而是使用exec命令将数据追加到现有文件
exec 3>>test13out 

6.8 重定向文件描述符

#!/bin/bash 
# storing STDOUT, then coming back to it 
exec 3>&1 
exec 1>test14out 
echo "This should store in the output file" 
echo "along with this line." 
exec 1>&3 
echo "Now things should be back to normal"


第一个exec命令将文件描述符3重定向到了文件描述符1(STDOUT)的当前位置,也就是显示器。这意味着任何送往文件描述符3 的输出都会出现在屏幕上。 
第二个exec命令将STDOUT重定向到了文件,shell现在会将发送给STDOUT的输出直接送往该文件。但是,文件描述符3仍然指向STDOUT原先的位置(显示器)。如果此时将输出数据发送给文件描述符3,则它仍然会出现在显示器上,即使STDOUT已经被重定向了。 

向STDOUT(现在指向一个文件)发送一些输出之后,第三个exec命令将STDOUT重定向到了文件描述符3的当前位置(现在仍然是显示器)。这意味着现在STDOUT又恢复如初了,即指向其原先的位置——显示器。 


6.9 创建读/写文件描述符

#!/bin/bash
# testing input/output file descriptor
exec 3<> testfile
read line <&3
echo "Read:$line"
echo "This is atest line" >&3

在这个例子中,exec命令将文件描述符3用于文件testfile的读和写。接下来,使用分配好的文件描述符,通过read命令读取文件中的第一行,然后将其显示在STDOUT中。最后,使用echo 语句将一行数据写入由同一个文件描述符打开的文件中。 
在运行脚本时,一开始还算正常。输出内容表明脚本读取了 testfile 文件的第一行。但如果在脚本运行完毕后查看testfile文件内容,则会发现写入文件中的数据覆盖了已有数据。 
当脚本向文件中写入数据时,会从文件指针指向的位置开始。read命令读取了第一行数据,这使得文件指针指向了第二行数据的第一个字符。当echo语句将数据输出到文件时,会将数据写入文件指针的当前位置,覆盖该位置上的已有数据。 


6.10 关闭文件描述符

如果创建了新的输入文件描述符或输出文件描述符,那么 shell会在脚本退出时自动将其关闭。然而在一些情况下,需要在脚本结束前手动关闭文件描述符。 
要关闭文件描述符,只需将其重定向到特殊符号&-即可。

exec 3>&- 

#!/bin/bash 
# testing closing file descriptors 
exec 3> test17file 
echo "This is a test line of data" >&3 
exec 3>&- 
echo "This won't work" >&3

一旦关闭了文件描述符,就不能在脚本中向其写入任何数据,否则shell会发出错误消息。

在关闭文件描述符时还要注意另一件事。如果随后你在脚本中打开了同一个输出文件,那么shell 就会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件。

#!/bin/bash 
# testing closing file descriptors 
exec 3> test17file 
echo "This is a test line of data" >&3 
exec 3>&- 
cat test17file 
exec 3> test17file 
echo "This'll be bad" >&3 

在向test17file 文件发送了字符串并关闭该文件描述符之后,脚本会使用cat 命令显示文件
内容。到这一步的时候,一切都还好。接下来,脚本重新打开了该输出文件并向它发送了另一个
字符串。在显示该文件的内容时,你能看到的就只有第二个字符串。shell覆盖了原来的输出文件。

6.11 抑制命令输出

ls -al > /dev/null

cat /dev/null > testfile

文件 testfile 仍然还在,但现在是一个空文件。这是清除日志文件的常用方法


6.12 记录消息

有时候,也确实需要将输出同时送往显示器和文件。与其对输出进行两次重定向,不如改用特殊的tee命令

tee 命令就像是连接管道的 T型接头,它能将来自 STDIN 的数据同时送往两处。一处STDOUT,另一处是tee命令行所指定的文件名。

#!/bin/bash 
# using the tee command for logging 
tempfile=test22file 
echo "This is the start of the test" | tee $tempfile 
echo "This is the second line of the test" | tee -a $tempfile 
echo "This is the end of the test" | tee -a $tempfile 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值