Bash Shell脚本编写入门
脚本退出命令
脚本的最后一部分通常是
exit 0
命令。在所有脚本中使用
exit
命令是一个好习惯。该命令用于退出脚本,并告知父shell脚本的执行情况。如果父shell读取到
exit 0
,就知道脚本执行成功;如果遇到除
exit 0
之外的其他情况,就知道脚本执行出现了问题。在更复杂的脚本中,还可以使用不同的退出代码,例如用
exit 1
作为通用错误信息,
exit 2
等用于指定特定条件未满足。
脚本执行方式
编写好第一个shell脚本后,就可以执行它了,执行方式有以下几种:
- 使其可执行并作为程序运行。
- 作为
bash
命令的参数运行。
- 引用脚本。
使脚本可执行
最常见的运行shell脚本的方法是使其可执行。以示例中的
hello
脚本为例,可使用以下命令:
chmod +x hello
使脚本可执行后,就可以像运行其他普通命令一样运行它。不过,脚本在目录结构中的位置会有一定限制。如果脚本在搜索路径中,只需输入脚本名即可运行;如果不在搜索路径中,则必须从脚本所在的精确目录运行。例如,如果
linda
在
/home/linda
目录下创建了一个名为
hello
的脚本,她可以使用
/home/linda/hello
命令来运行;如果她已经在
/home/linda
目录下,也可以使用
./hello
来运行,这里的点和斜杠表示从当前目录运行该命令。
提示
:不确定某个目录是否在搜索路径中?可以使用
echo $PATH
来查看。如果不在,可以通过重新定义路径来添加目录,例如要将
/something
目录添加到路径中,可以使用
PATH=$PATH:/something
。
作为
bash
命令的参数运行
第二种运行脚本的方式是将脚本名作为
bash
命令的参数。例如,示例脚本
hello
可以使用
bash hello
命令来运行。这种方式的优点是无需先使脚本可执行。但要注意,运行时需要使用脚本所在位置的完整引用,脚本必须在当前目录,或者需要使用脚本所在目录的完整引用。例如,如果脚本位于
/home/linda/hello
,而当前目录是
/tmp
,则应使用
bash /home/linda/hello
命令来运行。
引用脚本
第三种运行脚本的方式比较特殊,即引用脚本。引用脚本时,不会以子shell的方式运行,而是将其包含在当前shell中。如果脚本包含希望在当前shell中生效的变量,这种方式会很有用(在计算机启动时执行的脚本中经常会出现这种情况)。不过也可能会出现一些问题,例如在引用的脚本中使用
exit
命令会关闭当前shell。需要记住,
exit
命令是用于退出当前脚本,实际上是告知执行shell脚本已结束,需要返回到其父shell。因此,不建议引用包含
exit
命令的脚本。引用脚本有两种方式,以下是引用名为
settings
的脚本的示例:
. settings
source settings
这两种方式效果相同,具体使用哪种均可。
变量与输入的使用
脚本的灵活性很大程度上取决于变量的使用。变量是一个动态的值,其值通常取决于具体情况。脚本可以通过多种方式获取变量,例如执行命令、进行计算、将其指定为脚本的命令行参数或修改文本字符串等。
理解变量
变量是在某个地方定义并在后续灵活使用的值。可以在脚本中定义变量,也可以在shell中定义。定义变量的语法是
varname=value
,要获取变量的值,可以使用
echo
命令。以下是在命令行设置变量并使用其值的示例:
nuuk:~ # HAPPY=yes
nuuk:~ # echo $HAPPY
yes
注意
:上述方法适用于Bash和Dash shell,但并非所有shell都支持这种方式。例如,在
tcsh
中,需要使用
set
命令来定义变量,如
set happy=yes
会将变量
happy
的值设置为
yes
。
变量在计算机中起着非常重要的作用。计算机启动时会定义许多变量,在后续使用中会用到这些变量。例如,计算机名、登录的用户账户名以及搜索路径都存储在变量中。这些变量就是shell变量,也就是登录到shell时自动获取的环境变量。可以使用
env
命令获取计算机上设置的所有变量的完整列表。大多数环境变量使用大写字母,但这并非强制要求,环境变量也可以使用小写字母。
在shell脚本中使用变量有以下三个优点:
- 作为某个值的单一管理点。
- 作为用户以某种方式提供的值。
- 作为动态计算的值。
以下是一个简单的示例脚本,展示了变量的使用:
#!/bin/bash
#
# dirscript
#
# 一个简单的脚本,创建一个指定名称的目录
# 然后将$USER和$GROUP设置为该目录的所有者
# 最后将权限模式更改为770
DIRECTORY=/blah
USER=linda
GROUP=sales
mkdir $DIRECTORY
chown $USER $DIRECTORY
chgrp $GROUP $DIRECTORY
chmod 770 $DIRECTORY
exit 0
可以看到,在注释行之后,脚本首先定义了所有要使用的变量。这里将变量名全部使用大写字母,这样在阅读较长的脚本时更容易识别变量。在脚本的第二部分,通过在变量名前加上
$
符号来引用这些变量。
不过,这种方式使用变量有一个缺点,即比较静态。如果想要更动态地使用变量,可以在命令行执行脚本时将其作为参数指定。
变量、子shell和引用
定义变量时需要注意,变量仅在当前shell中定义。这意味着如果从当前shell启动一个子shell,该变量在子shell中将不存在;如果在子shell中定义一个变量,退出子shell返回父shell后,该变量也将不存在。以下示例展示了这种情况:
nuuk:~/bin # HAPPY=yes
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin # bash
nuuk:~/bin # echo $HAPPY
nuuk:~/bin # exit
exit
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin #
在某些情况下,可能希望设置一个在所有子shell中都可用的变量。这时可以使用
export
命令来定义变量。例如,以下命令将定义变量
HAPPY
,并确保从当前shell开始,在所有子shell中都可以使用该变量,直到下次重启计算机:
export HAPPY=yes
注意
:确保将变量定义包含在
/etc/profile
中,这样重启后新变量仍然可用。
以下示例展示了使用
export
命令后的情况:
nuuk:~/bin # export HAPPY=yes
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin # bash
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin # exit
exit
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin #
另一种与变量相关的常用技术是引用包含变量的文件。可以在计算机上保存一个包含变量的通用文件,例如以下
vars
文件:
HAPPY=yes
ANGRY=no
SUNNY=yes
将所有变量放在一个文件中的主要优点是,可以通过引用该文件在其他shell中使用这些变量。例如,可以使用以下命令引用
vars
文件:
. vars
注意
:
. vars
和
./vars
不同。
. vars
是将
vars
文件的内容包含在当前shell中,而
./vars
是从当前shell运行
vars
文件。前者不会启动子shell,而后者会。
以下示例展示了如何使用引用来包含通用配置文件中的变量:
nuuk:~/bin # echo $HAPPY
nuuk:~/bin # echo $ANGRY
nuuk:~/bin # echo $SUNNY
nuuk:~/bin # . vars
nuuk:~/bin # echo $HAPPY
yes
nuuk:~/bin # echo $ANGRY
no
nuuk:~/bin # echo $SUNNY
yes
nuuk:~/bin #
脚本参数的使用
使用脚本参数
运行脚本时,可以在命令行为脚本指定参数。以之前看到的
dirscript
脚本为例,可以在命令行使用参数来运行它,例如:
dirscript /blah
在脚本中,可以使用
$1
引用脚本启动时使用的第一个参数,使用
$2
引用第二个参数,依此类推,最多可以引用到
$9
。还可以使用
$0
引用脚本本身的名称。以下示例脚本展示了如何使用参数:
#!/bin/bash
#
# argscript
#
# 一个简单的脚本,展示如何使用参数
ARG1=$1
ARG2=$2
ARG3=$3
SCRIPTNAME=$0
echo The name of this script is $SCRIPTNAME
echo The first argument used is $ARG1
echo The second argument used is $ARG2
echo The third argument used is $ARG3
exit 0
以下是将
dirscript
脚本重写为使用命令行参数的示例:
#!/bin/bash
#
# dirscript
#
# 一个简单的脚本,创建一个指定名称的目录
# 然后将$USER和$GROUP设置为该目录的所有者
# 最后将权限模式更改为770
# 先提供目录名,然后是用户名,最后是组名。
DIRECTORY=$1
USER=$2
GROUP=$3
mkdir /$DIRECTORY
chown $USER $DIRECTORY
chgrp $GROUP $DIRECTORY
chmod 770 $DIRECTORY
exit 0
要执行上述脚本,可以使用以下命令:
dirscript /somedir kylie sales
这种方式使
dirscript
脚本更加灵活,但也存在一个重要的缺点,即参数的顺序可能会让用户混淆,因此提供清晰的脚本运行说明很重要。
计算脚本参数的数量
有时需要检查脚本提供的参数数量。例如,当期望特定数量的参数时,可以在运行脚本之前确保提供了所需数量的参数。可以使用
$#
来计算脚本提供的参数数量,
$#
实际上是一个计数器,用于显示运行脚本时使用的参数的确切数量。单独使用
$#
可能没有太大意义,但结合
if
语句就会很有用,例如可以在用户未提供正确数量的参数时显示帮助信息。以下是一个使用
$#
的示例脚本
countargs
:
nuuk:~/bin # cat countargs
#!/bin/bash
#
# countargs
# 一个简单的脚本,展示使用了多少个参数
echo the number of arguments is $#
exit 0
nuuk:~/bin # ./countargs a b c d e
the number of arguments is 5
nuuk:~/bin #.
引用所有脚本参数
到目前为止,看到的脚本都使用固定数量的参数。但如果参数数量事先未知,可以在脚本中使用
$@
或
$*
。这两个都可以引用脚本启动时指定的所有参数,但它们有区别。以下示例脚本
showargs
展示了它们的默认输出:
#!/bin/bash
# showargs
# 这个脚本展示启动时使用的所有参数
echo the arguments are $@
echo the arguments are $*
exit 0
如果使用参数
a b c d
启动该脚本,结果如下:
nuuk:~/bin # ./showargs a b c d
the arguments are a b c d
the arguments are a b c d
虽然从输出上看
$@
和
$*
似乎没有区别,但实际上
$*
将参数集合视为一个文本字符串,而
$@
将参数集合视为单独的字符串。
请求用户输入
另一种获取输入的优雅方式是直接请求用户输入。可以在脚本中使用
read
命令,当脚本执行到
read
命令时,会等待用户输入,并将输入内容存储在一个变量中。以下示例脚本
askinput
展示了如何请求用户输入并显示输入内容:
nuuk:~/bin # cat askinput
#!/bin/bash
#
# askinput
# 提示用户输入一些文本,然后显示输入内容
echo Enter some text
read SOMETEXT
echo -e "You have entered the following text:\t $SOMETEXT"
exit 0
nuuk:~/bin # ./askinput
Enter some text
hi there
You have entered the following text: hi there
nuuk:~/bin #
可以看到,脚本首先使用
echo
命令提示用户输入文本,然后使用
read SOMETEXT
命令等待用户输入,并将输入内容存储在变量
SOMETEXT
中。接着使用
echo -e
命令显示变量
SOMETEXT
的当前值,这里的
-e
选项允许使用一些特殊的格式字符,例如
\t
表示制表符。
在使用
echo -e
时,需要注意将包含特殊字符的文本放在双引号中,以防止shell在
echo
命令处理之前解释这些特殊字符。以下是不同命令的区别示例:
SYD:~ # echo \t
t
SYD:~ # echo "\t"
\t
SYD:~ # echo -e \t
t
SYD:~ # echo -e "\t"
SYD:~ #
使用
echo -e
时,可以使用以下特殊字符:
-
\0NNN
:ASCII码为NNN(八进制)的字符。
-
\\
:反斜杠。
-
\a
:警报(BEL或响铃代码)。
-
\b
:退格符。
-
\c
:抑制尾随换行符的字符。
-
\f
:换页符。
-
\n
:换行符。
Bash Shell脚本编写入门
Bash Shell脚本的综合应用与优化思路
在掌握了Bash Shell脚本的基本元素,如脚本执行、变量使用和参数处理之后,接下来探讨一些综合应用场景和优化思路,以提升脚本的效率和实用性。
脚本的模块化设计
为了提高脚本的可维护性和复用性,可以采用模块化设计。将不同功能的代码封装成独立的脚本或函数,然后在主脚本中调用这些模块。例如,将目录创建、权限设置等操作分别封装成函数,在
dirscript
中调用:
#!/bin/bash
# 创建目录函数
create_directory() {
mkdir /$1
}
# 设置目录所有者函数
set_owner() {
chown $1 $2
chgrp $3 $2
}
# 设置目录权限函数
set_permissions() {
chmod 770 $1
}
# 主脚本逻辑
DIRECTORY=$1
USER=$2
GROUP=$3
create_directory $DIRECTORY
set_owner $USER $DIRECTORY $GROUP
set_permissions $DIRECTORY
exit 0
通过这种方式,每个函数的功能明确,便于修改和复用。
错误处理与日志记录
在脚本中加入错误处理和日志记录机制,可以帮助我们更好地监控脚本的执行情况。例如,在创建目录时,如果目录已经存在,脚本可能会出错,我们可以捕获这个错误并记录日志:
#!/bin/bash
DIRECTORY=$1
USER=$2
GROUP=$3
# 尝试创建目录
if ! mkdir /$DIRECTORY 2>/dev/null; then
echo "Error: Directory /$DIRECTORY already exists." >> script.log
exit 1
fi
chown $USER $DIRECTORY
chgrp $GROUP $DIRECTORY
chmod 770 $DIRECTORY
echo "Directory /$DIRECTORY created successfully." >> script.log
exit 0
在这个例子中,
2>/dev/null
将错误输出重定向到空设备,避免在屏幕上显示错误信息。如果创建目录失败,将错误信息记录到
script.log
文件中,并以错误代码
1
退出脚本;如果成功,将成功信息记录到日志文件中。
脚本的性能优化
对于一些需要处理大量数据或执行复杂计算的脚本,性能优化至关重要。以下是一些常见的优化方法:
-
减少不必要的命令执行
:避免在循环中重复执行相同的命令,可以将结果缓存起来。
-
使用高效的数据结构
:例如,使用数组来存储和处理数据,避免频繁的文件读写操作。
-
并行处理
:对于一些可以并行执行的任务,可以使用
&
符号将其放到后台执行,提高脚本的执行效率。
脚本的安全性考虑
在编写和执行Bash Shell脚本时,安全性是一个不可忽视的问题。以下是一些常见的安全建议:
-
限制脚本的执行权限
:只给脚本必要的执行权限,避免脚本被滥用。例如,使用
chmod
命令设置合适的权限。
-
输入验证
:对用户输入的参数进行验证,防止恶意输入导致脚本执行异常或安全漏洞。例如,在
dirscript
中,可以验证目录名、用户名和组名是否合法。
-
避免使用硬编码的敏感信息
:如密码、密钥等,应使用环境变量或配置文件来存储这些信息。
总结与展望
Bash Shell脚本是Linux和Unix系统中非常强大的工具,通过合理使用脚本的执行方式、变量、参数和输入输出等功能,可以实现各种自动化任务。在实际应用中,我们可以根据具体需求对脚本进行模块化设计、错误处理、性能优化和安全防护,以提高脚本的质量和可靠性。
未来,可以进一步探索Bash Shell脚本与其他工具和技术的结合,如数据库操作、网络编程等,实现更复杂的功能。同时,也可以学习其他脚本语言,如Python、Perl等,以满足不同场景的需求。
以下是一个简单的mermaid流程图,展示了
dirscript
脚本的执行流程:
graph TD;
A[开始] --> B[获取参数];
B --> C{目录是否存在};
C -- 是 --> D[记录错误并退出];
C -- 否 --> E[创建目录];
E --> F[设置所有者];
F --> G[设置权限];
G --> H[记录成功信息];
H --> I[结束];
D --> I;
通过以上内容,我们对Bash Shell脚本的编写有了更深入的了解,希望这些知识能帮助你在实际工作中更好地运用脚本实现自动化任务。
超级会员免费看
33万+

被折叠的 条评论
为什么被折叠?



