李守中

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


Last Update: 2023-08-30 Wed 14:46

Contact: [email protected]     Generated by: Emacs 27.1 (Org mode 9.3)

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议