使用 shell 脚本,可以在某些场合重复使用,提高利用率。
- 脚本的第一行通常是指定解释器,即这个脚本必须通过什么解释器执行。一般称为
Shebang
#!/bin/sh
- 如果它没有放在 bin 目录下,就无法执行,为了保险起见,使用以下方式执行
#!/usr/bin/env sh
- 如果不指定 shebang,那么就需要指定脚本解释器
bash ...
关于脚本执行的前提条件就是,它需要获得这个权限
# 给所有用户执行权限
➜ chmod +x script.sh
# 给所有用户读权限和执行权限,拥有者有所有权限
➜ chmod +rx script.sh
# 或者
➜ chmod 755 script.sh
# 只给脚本拥有者读权限和执行权限
➜ chmod u+rx script.sh
# 或者
➜ chmod 700 script.sh
脚本调用时,一般需要指定脚本路径,如果我们将其加入 $PATH (环境变量)中,就不需要指定路径
export PATH=$PATH:~/bin
-
上面命令改变环境变量
$PATH
,将~/bin
添加到$PATH
的末尾。在 bin 下的所有脚本文件可以直接执行,而不需要输入路径 -
可以将这一行加到
~/.bashrc
文件里面,然后重新加载一次.bashrc
,这个配置就可以生效了。- 如果是 zsh,那么需要在 .zshrc 中配置
- 然后使用 source 重新启动配置
source ~/.bashrc
env 命令总是指向
/usr/bin/env
文件,此文件总是在 /usr/bin 目录下
- 下面语法的意思就是让 shell 咋着 $PATH 环境变量中第一个匹配的 node(name)。
#!/usr/bin/env node
env
命令的参数-i
或 --ignore-environment:不带环境变量启动-u
, --unset=NAME:从环境变量中删除一个变量
$0
:脚本文件名,即script.sh
$1~$9
:对应脚本的第一个参数到第九个参数。$#
:参数的总数。$@
:全部的参数,参数之间使用空格分隔。$*
:全部的参数,参数之间使用变量$IFS
值的第一个字符分隔,默认为空格,但是可以自定义
#!/usr/bin/env bash
echo "all the argumengs" $@
echo "all the argumengs**" $*
echo "script name" $0
#all the argumengs -o for bar
#all the argumengs** -o for bar
#script name ./script.sh
- 我们也可以使用 for 循环打印参数。如果传入的参数是在
""
中的,那么会视为一个整体,如 "a b" ->a b
for i in "$@"; do
echo $i
done
shift
命令可以改变脚本参数,每次执行都会移除脚本当前的第一个参数($1),这使后main的参数会向前移一位,如$2
->$1
...
echo "一共输入了 $# 个参数"
while [ "$1" != "" ]; do
echo "剩下 $# 个参数"
echo "参数:$1"
shift
done
- 如果指定整数,则是当前要移除的参数个数
# $3 -> $1
shift 2
getopts 命令拥于脚本内部,解析复杂的脚本命令行参数。通常与 while 循环一起使用,取出脚本所有的带有前置连词线(
-
)的参数
getopts optstring name
- getopts 接受两个参数,第一个参数表示脚本的所有的连字符参数。例如,某个脚本可以有三个配置项参数
-l
、-h
、-a
,其中只有-a
可以带有参数值,而-l和-h是开关参数,那么getopts的第一个参数写成lha:
,顺序不重要。- a 后面带有
:
,表示-a
后带有参数值。getopts
的第二个参数name
是一个变量名,用来保存当前取到的配置项参数
- a 后面带有
while getopts 'lha:' OPTION; do
case "$OPTION" in
l)
echo "linuxconfig"
;;
h)
echo "h stands for h"
;;
a)
avalue="$OPTARG"
echo "The value provided is $OPTARG"
;;
?)
echo "script usage: $(basename $0) [-l] [-h] [-a somevalue]" >&2
exit 1
;;
esac
done
shift "$(($OPTIND - 1))"
?
: 指用户输入了没有指定的参数,例如-x
$OPTARG
: 指的是保存的参数值,如-a foo
,那么 $OPTARG 就是 foo- 注意:只要遇到不带连词线的参数,那么
getopts
就会失败 - 可以将多个连词写到一起,比如
./script.sh -la fw
,但是不能写成-al
,如果不带有参数,位置可以互换
- 注意:只要遇到不带连词线的参数,那么
$OPTIND
: 在getopts
开始执行前是1
,然后每次执行就会加 1(在处理 -a 之后的参数是也会加 1)- 在 while 循环结束后,
$OPTIND - 1
就是已经处理的连词线参数个数,使用 shift 命令将这些参数移除。
- 在 while 循环结束后,
-
和--
开头的参数,会被 Bash 当作配置项解释。但是有时候,它们就是实体的一部分
# 需要使用 -- 读取 --file 文件
cat --file
cat -- --file
- 如果要确保某个变量不会被当作配置项解释,就要在它前面放上参数终止符
--
。
myPath="-l"
ls -- $myPath
#ls: 无法访问'-l': 没有那个文件或目录
- 使用正则查找的时候也可以使用
grep -- "--hello" example.txt
用于退出当前的脚本,并向终端输出一个退出值
- exit命令后面可以跟参数,该参数就是退出状态。
- 0 表示正常,1 表示发生错误,2 表示用法不对。
- 126 表示不是可执行脚本,127 表示命令没有发现。如果脚本被信号 N 终止,则退出值为 128 + N。
if [ $(id -u) != "0" ]; then
echo "根用户才能执行当前脚本"
exit 1
fi
id -u
返回用户的 ID,一旦用户的 ID 不等于 0(即不是 root),就会失败退出
每个命令行执行结束后都会返回一个结果,如果成功,即是
0
cd /path/to/somewhere
# $? 指的是上一个命令行返回的结果,
# 即,如果有 /path/to/somewhere 这个目录,则返回 0
if [ "$?" = "0" ]; then
rm *
else
echo "无法切换目录!" 1>&2
exit 1
fi
- 也可以使用更简洁的方式去写
cd /path/to/somewhere && rm *
cd /path/to/somewhere || exit 1
source
命令用于执行一个脚本,通常用于重新加载一个配置文件
source .bashrc
- source 命令最大的特点是在当前 Shell 执行脚本,不像直接执行脚本时,会新建一个子 Shell
- 例如,先创建一个 test 脚本
#!/bin/bash
# test.sh
echo $foo
- 然后打印输出变量。当前 Shell 的变量 foo 并没有 export,所以直接执行无法读取,但是 source 执行可以读取
# 当前 Shell 新建一个变量 foo
$ foo=1
# 打印输出 1
$ source test.sh
1
# 打印输出空字符串
$ bash test.sh
当使用 shell 引用外部库的时候,我们可以直接使用内部的函数
#!/bin/bash
source ./lib.sh
function_from_lib
- source 还有一个简写形式(
.
)
. .bashrc
- alias: 显示所有别名
- alias NAME=DEFINITION:为原始命令设置别名
- unalias NAME: 解除别名
read [-options] [var...]
read 命令用于将用户的输入存入一个变量
#!/bin/bash
echo -n "输入>>"
read text
echo "你的输入:$text"
- 你也可以同时定义多个变量,但是用户输入时不同变量之间也需要使用空格隔开才可以
echo -n "输入>>"
read text text2
echo "你的输入:$text $text2"
- 如果 read 后没有设置变量,那么用户输入的所有变量都会被环境变量
REPLY
包含
#!/bin/bash
echo -n "输入>>"
read
echo "你的输入:$REPLY"
- 使用 read 也可以读取文件,done 之后的是定向符(
<
),将文件的内容导向read
命令,每次读取一行,存入变量 muline
#!/bin/bash
filename='/etc/hosts'
while read myline
do
echo "$myline"
done < $filename
options
-
-t
: 指定如果超时多少秒,自动关闭 -
-p
: 指定用户输入的信息read -p "输入一个或多个值>>" echo "$REPLY"
-
-a
: 把用户输入赋值给一个数组,从零号位置开始read -a test # a b c echo ${people[2]} # c
-
-n
: 指定只读取若干个字符作为变量值read -n 3 letter # abcdef echo $letter # abc
-
-e
: 该参数允许用户输入的时候可以使用 tab 补全 -
-r
: raw 模式,表示不把用户输入的\
作为转义字符解释 -
-s
: 使用户的输入不现实屏幕上,用于输入密码或保密信息 -
-u fd
: 使用文件描述符 fd 作为输入 -
-d limit
: 定义字符串 limit 的第一个字符作为用户输入的结束,而不是一个换行符#!/bin/bash # 如果用户输入 f 就会结束,而不是换行符 echo -n "输入>>" read -d fw echo "你的输入:$REPLY"
read
命令读取的值,默认是以空格分隔。可以通过自定义环境变量IFS
,修改分隔符
- 如果把IFS定义成冒号(
:
)或分号(;
),就可以分隔以这两个符号分隔的值,这对读取文件很有用。
#!/bin/bash
# read-ifs: read fields from a file
FILE=/etc/passwd
read -p "Enter a username > " user_name
file_info="$(grep "^$user_name:" $FILE)"
if [ -n "$file_info" ]; then
IFS=":" read user pw uid gid name home shell <<< "$file_info"
echo "User = '$user'"
echo "UID = '$uid'"
echo "GID = '$gid'"
echo "Full Name = '$name'"
echo "Home Dir. = '$home'"
echo "Shell = '$shell'"
else
echo "No such user '$user_name'" >&2
exit 1
fi