Bash 5 脚本常用命令
Table of Contents
1 echo 命令输出一行文本
echo
命令的作用是在屏幕输出一行文本,这个命令将它的参数按原样输出。如果想要输出的文本包括换行符,需要把多行文本放在引号内。
$ echo hello world hello world $ echo "<HTML> <HEAD> <TITLE>Page Title</TITLE> </HEAD> <BODY> Page body. </BODY> </HTML>" <HTML> <HEAD> <TITLE>Page Title</TITLE> </HEAD> <BODY> Page body. </BODY> </HTML>
echo -n
可以取消被输出的文本的末尾的回车符,使得下一个提示符紧跟在输出内容的后面。
$ echo -n hello world hello world$ $ echo a;echo b a b $ echo -n a;echo b ab
echo -e
会转译双引号和单引号内的特殊字符 (比如换行符 \n
)。如果不使用 -e
参数,即默认情况下,引号会让特殊字符变成普通字符,按原样输出。
$ echo "Hello\nWorld" Hello\nWorld $ echo -e "Hello\nWorld" Hello World $ echo -e 'Hello\nWorld' Hello World
2 read 命令读取用户输入
2.1 基础用法
命令格式: read [-options] [<variable> ...]
。用户按下回车键表示输入结束。只能解析标准输入。
options
是配置项, variable
是保存输入数值的一个或多个变量名。如果没有提供变量名,环境变量 $REPLY
会包含用户输入的一整行数据。如果用户的输入项少于 read 变量数目,那么额外的变量值为空。如果用户的输入项多于定义的变量,那么多余的输入项会包含到最后一个变量中。
给几个例子:
#!/bin/bash
echo -n "Input some text >"
read text
echo "Your Input >$text"
#!/bin/bash
echo "Enter your firstname and lastname"
read FN LN
echo "Hi! $LN, $FN !"
$ test.sh Enter your firstname and lastname aa bb cc dd Hi! bb cc dd, aa !
read 命令除了读取键盘输入,可以用来读取文件:
#!/bin/bash
filename='/etc/hosts'
# 每次读取一行,存入变量myline,直到文件读取完毕
while read myline
do
echo "$myline"
done < $filename
2.2 命令参数
read -t <number> [<variable> ...]
可以设置超时秒数。如果超过了指定时间,用户仍然没有输入,脚本将放弃等待,继续向下执行。环境变量 $TMOUT
也可以起到同样作用。
#!/bin/bash
# expire in 3s way 1
echo -n "Input some text >"
if read -t 3 response; then
echo "Got the input"
else
echo "No input"
fi
# expire in 3s way 2
TMOUT=3
echo -n "Input some text >"
if read -t 3 response; then
echo "Got the input"
else
echo "No input"
fi
read -p <message> [<variable> ...]
指定提示信息 (输出提示信息后不换行)。
read -p "Enter one or more values >"
echo "REPLY = '$REPLY'"
read -a <array_name>
把用户的输入赋值给一个数组,从零号位置开始。
$ read -a people alice duchess dodo $ echo ${people[2]} dodo
read -n <number> [<variable> ...]
指定只读取若干个字符作为变量值,而不是整行读取。
$ read -n 3 letter abcdefghij $ echo $letter abc
read -e [<variable> ...]
允许用户输入时使用 readline 库提供的快捷键,比如自动补全。
#!/bin/bash
echo Please input the path to the file:
read -e fileName
echo $fileName
还有一些不常用的参数:
read -d <delimiter>
: 定义字符串<delimiter>
的第一个字符作为用户输入的结束,而不是一个换行符。read -r
: 不把用户输入的反斜杠解释为转义字符。read -s
: 用户的输入不显示在屏幕上,这常常用于输入密码或保密信息。read -u fd
: 使用文件描述符作为输入。
2.3 IFS 变量
read 命令读取输入时,默认以空格作为多项输入的分隔符号。可以通过自定义环境变量 $IFS
(内部字段分隔符 Internal Field Separator) 修改分隔标志。
把 $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
IFS 被设为冒号,用来分解 /etc/passwd 文件的一行。IFS 的赋值命令和 read 命令写在一行,表示 IFS 的改变仅对后面的命令生效,命令执行后 IFS 会自动恢复原来的值。如果不写在一行,就要采用下面的写法:
OLD_IFS="$IFS"
IFS=":"
# read 命令只能解析标准输入
# <<< 表示 Here 字符串,它将变量值转为标准输入
read user pw uid gid name home shell <<< "$file_info"
IFS="$OLD_IFS"
如果 IFS 设为空字符串,就等同于将整行读入一个变量:
#!/bin/bash
# 逐行读取文件,行存入变量line
# 打印出来以后再读取下一行
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
3 type 命令查看命令来源
type
命令用来判断命令的来源 (内置命令或外部程序)。
$ type echo echo is a shell builtin $ type type type is a shell builtin $ type ls ls is hashed (/bin/ls)
type -a <command>
查看一个命令的所有定义:
$ type -a echo echo is shell builtin echo is /usr/bin/echo echo is /bin/echo
type -t <command>
返回命令的类型: 别名 alias
,关键词 keyword
,函数 function
,内置命令 builtin
或者文件 file
。
$ type -t bash file $ type -t if keyword
4 export 命令传递变量到子 Shell
用户创建的变量默认仅作用于当前 Shell,子 Shell 读取不到父 Shell 定义的变量。
要把变量从父 Shell 传递到子 Shell,需要用 export
命令导出要被传递的变量。被 export
导出的变量,对于子 Shell 来说就是环境变量。使用 unset <var_name>
删除被导出的变量即可取消 export
操作。
# 2 步完成
var=foo
export var
# 1 步完成
export var=foo
子 Shell 对继承来的变量的操作不影晌父 Shell:
# 输出变量 $foo $ export foo=bar # 新建子 Shell $ bash # 读取 $foo $ echo $foo bar # 修改继承的变量 $ foo=baz # 退出子 Shell $ exit # 读取 $foo $ echo $foo bar
5 mktemp 命令创建临时文件与目录
执行 mktemp
如果不带参数,就会在 /tmp 文件夹下创建一个名称随机的文件。但在创建这个文件之前,它不检查是否有同名文件。
命令行可以直接使用:
$ mktemp /tmp/tmp.Ic2jwTdDut $ ls -alh /tmp/tmp* -rw------- 1 lsz lsz 0 Feb 13 16:43 /tmp/tmp.Ic2jwTdDut
但多用于脚本中:
#!/bin/bash # || exit 1 保证创建失败时退出脚本 TMPFILE=$(mktemp) || exit 1 echo "Temp file is $TMPFILE"
它的常用参数有:
-d
创建一个临时目录。-p
指定临时文件所在的目录。默认情况下,文件或目录被创建于环境变量$TMPDIR
指定的目录下,如果这个变量为空,那么使用 /tmp 目录。-t
指定文件名模板。模板的末尾必须至少包含 3 个连续的大写X
字符,表示随机字符。建议至少使用 6 个X
。默认的文件名是tmp.
后接 10 个随机字符。
$ mktemp -d -t lsz.temp.XXXXXX -p /home/lsz /home/lsz/lsz.temp.YF8o7G $ ls -alh /home/lsz/ | grep lsz.temp drwx------ 2 lsz lsz 4.0K Feb 13 16:52 lsz.temp.YF8o7G
要在脚本退出时删除临时文件可以使用 trap
命令。
6 trap 命令响应系统信号
最常见的系统信号就是 SIGINT (中断),即按下键盘 Ctrl+c
所产生的信号。
trap -l
可以查看所有的系统信号。
trap的命令格式如下。
$ trap [action] [SIG1] [SIG2] ...
action
是一个 Bash 命令, SIG
常用的有以下几个:
HUP
: 编号 1,脚本与所在的终端脱离联系。INT
: 编号 2,用户按下Ctrl+c
意图让脚本终止运行。QUIT
: 编号 3,用户按下Ctrl+/
,意图退出脚本。KILL
: 编号 9,该信号用于杀死进程。TERM
: 编号 15,这是kill
命令发出的默认信号。EXIT
: 编号 0,这不是系统信号,而是 Bash 脚本特有的信号,不管什么情况,只要退出脚本就会产生。
比如 trap 'rm -f "$TMPFILE"' EXIT
意为脚本遇到 EXIT 信号时,就会执行 rm -f "$TMPFILE"
。
trap 命令的常见使用场景,就是在 Bash 脚本中指定退出时执行的清理命令。
#!/bin/bash
trap 'rm -f "$TMPFILE"' EXIT
TMPFILE=$(mktemp) || exit 1
ls /etc > $TMPFILE
if grep -qi "kernel" $TMPFILE; then
echo 'find'
fi
注意: trap 只会捕获 trap 命令之后的语句产生的信号,所以 trap 命令必须放在脚本的开头。
如果 trap 需要触发多条命令,可以封装一个函数。
function egress {
command1
command2
command3
}
trap egress EXIT