文件描述符及重定向
- 文件描述符是与文件输入、输出相关联的整数,它们用来跟踪已打开的文件。最常见的文件描述符是stdin(0,标准输入)、stdout(1,标准输出)和stderr(2,标准错误)。我们甚至可以将某个文件描述符的内容重定向到另一个文件描述符中。
- 用下面方法可以将输出文本重定向或保存到一个文件中:
$ echo "This is a sample text 1" > temp.txt
- 这种方法通过截断文件的形式,将输出文本存储到文件temp.txt中,也就是说在把echo命令的输出写入文件之前,temp.txt中的内容首先会被清空。
- 将文本追加到目标文件中,可以使用“>>”
$ echo "This is a sample text 2" >> temp.txt
- 当命令输出错误信息时,stderr信息就会被打印出来。
$ ls + > out.txt;
#此命令会将stderr文本打印到屏幕上,而不是文件中(而且因为并没有stdout的输出,所以out.txt没有内容)$ ls + 2> out.txt;
#此命令正常运行,我们将stderr重定向到out.txt。$ cmd 2>stderr.txt 1>stdout.txt;
#将stderr单独重定向到一个文件,将stdout重定向到另一个文件。- 将stderr转换成stdout,使得stderr和stdout都被重定向到同一个文件中:
$ cmd &> output.txt;
- 有时候,输出中可能包含一些不必要的信息(如调试信息)。如果不想让终端中充斥着有关stderr的繁枝末节,可以将stderr的输出重定向到
/dev/null
,保证一切都会被清除得干干净净。
$ cmd 2>/dev/null;
#此时stdout正常,stderr重定向到/dev/null
。
- 通过tee命令可以既将数据重定向到文件,又提供一份重定向数据的副本作为后续命令的stdin。tee只能从stdin中读取,所以stderr中的内容不会出现在文件中。
$ cat a* | tee out.txt | cat -n
- 默认情况下,tee命令会将文件覆盖,但它提供了一个-a选项,用于追加内容。
$ cat a* | tee -a out.txt | cat -n
- 我们可以使用stdin作为命令参数,只需要将-作为命令的文件名参数即可:
$ echo who is this | tee -
- 将文件重定向到命令
$ cmd < file
- 将脚本内部的文本块进行重定向
$ cat<<EOF>log.txt
- 这种情况下,可以一直输入文本,直到遇到下一个
EOF
。然后这些输入的内容会作为cat
的参数,并输出到log.txt
文件中。
数组和关联数组
- 数组是Shell脚本中非常重要的组成部分,它借助索引将多个独立的数据存储为一个集合。普通数组只能用整数作为数组索引,关联数组支持使用字符串作为数组索引。
- Bash从4.0以后才开始支持关联数组。
- 定义数组的方法有很多种
$ array_var=(1 2 3 4 5 6)
$ array_var[0]="test1";array_var[1]="test2";
- 打印出特定索引的数组元素内容:
$ echo ${array_var[0]};
$ index=5; echo ${array_var[$index]}
- 以清单形式打印出数组中的所有值:
$ echo ${array_var[*]}
$ echo ${array_var[@]}
- 打印数组长度:
$ echo ${#array_var[*]}
- 定义关联数组
$ declare -A ass_array;
- 将元素添加到关联数组中:
$ ass_array=([index1]=val1 [index2]=val2);
$ ass_array[index1]=val1;ass_array[index2]=val2;
- 列出数组索引(对于普通数组和关联数组均可)
$ echo ${!array_var[*]}
$ echo ${!array_var[*]}
使用别名
- 别名就是一种快捷方式,以省去用户输入一长串命令序列的烦恼。
- 我们可以使用alias命令创建别名。
$ alias new_command="command sequence";
- alias命令的作用只是暂时的,一旦关闭当前终端,所有设置过的别名就失效了。为了使别名设置一直保持作用,可以将它放入
~/.bashrc
文件中,因为每当一个新的shell进程生成时,都会执行~/.bashrc
中的命令。
$ echo 'alias cmd="command seq"' >> ~/.bashrc
- 删除别名:
- 将其对应的语句(如果有的话)从
~/.bashrc
中删除; - 使用
unalias
命令 $ alias example=
#取消名为example的别名
- 将其对应的语句(如果有的话)从
- 创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的设置取代。
- 有时别名也会造成安全问题
- 对别名进行转义。alias命令能够为任何重要的命令创建别名,不过你未必总是希望用别名来执行这个命令。我们可以将希望使用的命令进行转义,从而忽略当前定义的别名。\对命令实施转义,是我们可以执行原本的命令,而不是这些命令的别名替身。在不可信环境下执行特权命令,通过在命令前加上\来忽略可能存在的别名设置总是一个不错的安全实践。因为攻击者可能已经将一些别有用心的命令利用别名伪装成了特权命令。
$ \command
- 对别名进行转义。alias命令能够为任何重要的命令创建别名,不过你未必总是希望用别名来执行这个命令。我们可以将希望使用的命令进行转义,从而忽略当前定义的别名。\对命令实施转义,是我们可以执行原本的命令,而不是这些命令的别名替身。在不可信环境下执行特权命令,通过在命令前加上\来忽略可能存在的别名设置总是一个不错的安全实践。因为攻击者可能已经将一些别有用心的命令利用别名伪装成了特权命令。
获取终端信息
tput
和stty
是两款终端处理工具- 获取终端的行数和列数
$ tput cols
$ tput lines
- 打印出当前终端名
$ tput longname
- 将光标移到坐标(100,100)处
$ tput cup 100 100
- 设置终端背景色
$ tputsetb n;
#n在0到7之间取值
- 设置文本前景色
$ tputsetf n;
#n在0到7之间取值
- 设置文本样式为粗体
$ tput bold
- 设置下划线的起止
$ tput smul
$ tput rmul
- 删除从当前光标位置到行尾的所有内容
$ tput ed
- 存储光标位置
$ tput sc
- 恢复光标位置
$ tput rc
- 输入密码时,不应该显示输入内容,我们可以使用stty来实现
$ stty -echo
- 选项
-echo
禁止将输出发送到终端,而选项echo
则允许发送输出。
获取、设置日期和延时
- 在类Unix系统中,日期被存储成一个整数,其大小为自世界标准时间(UTC)1970年1月1日0时0分0秒起所流逝的秒数。这种计时方式称为纪元时或Unix时间。
- 读取日期
$ date
- 打印纪元时(Unix时间):
$ date +%s
- 我们可以从给定格式的日期串中得出对应的Unix时间。
$ date --date "Thu Nov 18 08:07:21 IST 2010" +%s
$ date --date "Jan 20 2001" +%A
- 用格式串组合+作为date命令的参数,可以打印出对应格式的日期
$ date "+%d %B %Y"
- 设置日期和时间
$ date -s "格式化的日期字符串"
- 有时候需要检查一组命令所花费的时间,可以使用如下代码
start=$(date +%s);
commands;
statements;
end=$(date +%s);
difference=$((end - start))
- 1
- 2
- 3
- 4
- 5
- 另一种检查命令所花费时间的方法是使用time来得到执行脚本所花费的时间。
- 编写以循环方式运行的监视脚本时,设置时间间隔是必不可少的,我们可以使用sleep命令来在脚本中生成延时。
$ sleep 1; #sleep no_of_seconds
调试脚本
- 我们可以使用Bash内建的调试工具,或者按照易于调试的方式编写脚本。
- 使用选项-x,启用shell脚本的跟踪调试功能。运行带有-x标志的脚本可以打印出所执行的每一行命令以及当前状态。
$ sh -x script
$ bash -x script.sh
- 使用
set -x
和set +x
对脚本进行部分调试。脚本会只运行set -x
与set +x
之间的部分,因为它们对调试区域进行了限制。 前面两种调试手段是Bash内建的,它们通常以固定的格式生成调试信息。但是在很多情况下我们需要自定义格式显示调试信息。这可以通过传递_DEBUG环境变量来建立这类调试风格。
#!/bin/bash
function DEBUG()
{
["$_DEBUG" == "on" ] && $@ || :
}
for i in {1 .. 10}
do
DEBUG echo $i
done
# 可以将调试功能设置为`on`来运行上面的脚本
`$ _DEBUG="on" ./script.sh`
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
-x
表示将脚本执行过的每一行都输出到stdout,不过我们可能只关注脚本的某些部分的命令及参数的打印输出。针对这种情况,可以在脚本中使用set builtin来启用或禁止调试打印。set -x
:在执行时显示参数和命令。set +x
:禁止调试。set -v
:当命令进行读取时显示输入。set +v
:禁止打印输入。
- 我们可以利用shebang来进行调试。
a.#!/bin/bash
–>#!/bin/bash -xv
b. 这样一来,不用任何选项就可以启用调试功能了。