最近才开始买VPS搭建个人blog玩玩,虽说时间不是很长,但我个人觉得在这段折腾的时间里,个人还是学到了很多东西的,比方说很多shell脚本的问题,当你没有遇到这类问题的时候,即便你很聪明,在第一次碰到这个问题时,也很难下手,比方说这次记录的:”/bin/sh: 1: history: not found”
因为之前碰到过几次VPS不稳定{其实也因为我喜欢重装、网络连接不稳定、分析问题不够冷静彻底等原因}而导致了数据经常性的丢失,有些数据or内容因为有备份丢了还不算什么,但是有些内容,特别是某些只在VPS上修改过了的脚本丢了那我就非常心痛了,一来是有些修改只有在当时的环境中才会存在,其次这些修改真的是一个解决问题/难题的过程记录,丢了的话就很难找回来了,在痛定思痛之后我决定每天凌晨的时候进行自动备份{将任务写入crontab就行},备份的除了脚本还有我使用putty远程登陆过程中执行过的命令,修改了HISTTIMEFORMAT格式(HISTTIMEFORMAT=”@%F_%T “;export HISTTIMEFORMAT)之后还是显得非常直观的,可以清楚的看到我在什么时候执行了什么命令,不论是事后发现问题还是安全审计,这都是不错的一个改进,但是问题也就随之而来:
第一次:
直接将命令写入crontab(输入命令:crontab -e之后,编辑内容,添加一行内容:59 23 * * * history | mutt [email protected] -s “history_bak”,但是第二天在查看邮件内容的时候发现是空的,而且在”/var/mail/root”中有一行提示:/bin/sh: 1: history: not found)
第二次:
想着先将history的内容导出到一个txt文件,然后以邮件附件的形式发送出去。但是,依旧报错:/bin/sh: 1: history: not found 附件中的文本文件内容为空。
第三次:
决心把问题抓出来,于是开始搜索:
- site:stackoverflow.com /bin/sh: 1: history: not found
- ubuntu bash history no output site:stackoverflow.com
终于在以下内容中找到了问题所在:
History is not loaded for a non-interactive shell.(history命令在非交互式shell的情况下是没有加载的) You can either
tail
the history file (~/.bash_history
) or turn on history and load it.set -o history history -r
The full commands needed to do this from the remote host is as follows:
ssh host 'HISTFILE=~/.bash_history; history -r; history' | tail
即,history命令无法在一个非交互式的shell中执行,所以,如果想要达到我的目的,有两种方法:
- 直接使用~/.bash_history文件内容,但因为我添加了命令执行时间戳的功能,文件中显示的时间是Unix格式的,不直观,所以暂时不推荐;
- 在非交互式shell中加载/打开history命令,使用”set -o history”命令(如果经常性需要,可以将它添加到/etc/rc.local或~/.bashrc中)。
第二种方法的一个示例:
在脚本内部添加了”set -o history”命令
如果全局添加”set -o history”命令的话,估计安全性是个很大的问题(默认情况下history命令记录的都是交互式shell中执行的命令列表,而shell脚本中的命令执行属于非交互式的,所以,执行shell脚本时,脚本里面执行的命令不会出现在history命令显示的结果中),所以,想想我还是算了吧,使用~/.bash_history文件也可以的。
- bash – Why is the bash_history always coming up the same and not updating using non-login shells?
- Bash Reference Manual: The Set Builtin
- Bash Reference Manual: Bash History Facilities
================================================
在找到答案之前走的一些弯路:
关于-/bin/sh:xx(命令) not found 的几种原因:
- $PATH下没有这个命令;
- $PATH下有这个命令,只是执行权限不够,或者程序执行权限不够;
- 程序需要的静态库或者动态库没有。
上面的内容一看其实是非常有道理的,但是,刚开始我也就这么被带进沟里面了o(╯□╰)o
查找history文件:
# find / -name history #没有任何输出{在我的Ubuntu12.04 x32系统上 # man history #没有-r选项 # man bash #这里面的内容比较多,但是也确实太多了……
后来在搜索的过程中,才想到:因为history命令属于Linux的内建(built-in)命令,用find查找不到也就很正常了(关于如何知道Linux有哪些内建命令,请自行搜索or翻至文章最底部)
一些参考链接:
- Shell 脚本时显示declare not found的问题
- 执行shell脚本中declare: not found
- Ubuntu 下运行 Shell 脚本时显示 “declare not found” 的问题
- “source:not found” in Ubuntu10.10
- http://stackoverflow.com/questions/12468889/bash-script-error-function-not-found-why-would-this-appear
- http://stackoverflow.com/questions/13702425/source-command-not-found-in-sh-shell
====
linux中的set命令:”set -e”与”set -o pipefail”
工作中经常在shell脚本中看到set的这两个用法,但就像生活中的很多事情,习惯导致忽视,直到出现问题才引起关注。
1. set -e
set命令的-e参数,Linux中的说明如下:
“Exit immediately if a simple command exits with a non-zero status.”
也就是说,在”set -e”之后出现的代码,一旦出现了返回值非零,整个脚本就会立即退出。有的人喜欢使用这个参数,是出于保证代码安全性的考虑。但有的时候,这种美好的初衷,也会导致严重的问题。
2. set -o pipefail
对于set命令-o参数的pipefail选项,Linux里是这样解释的:
“If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status,or zero if all commands in the pipeline exit successfully. This option is disabled by default.”
设置了这个选项以后,包含管道命令的语句的返回值,会变成最后一个返回非零的管道命令的返回值。听起来比较绕,其实也很简单:
$ vim set_o_pipefail.sh $ cat set_o_pipefail.sh #!/bin/bash set -o pipefail ls ./a.txt |echo "hi" >/dev/null echo $? $ $ bash set_o_pipefail.sh ls: cannot access ./a.txt: No such file or directory 2 $ $ vim set_o_pipefail.sh $ cat set_o_pipefail.sh #!/bin/bash #set -o pipefail ls ./a.txt |echo "hi" >/dev/null echo $? $ $ bash set_o_pipefail.sh ls: cannot access ./a.txt: No such file or directory 0 $
- 参考文章:Bash Reference Manual: The Set Builtin
- 一个关于history命令使用方法的超赞文章:
- 15 Examples To Master Linux Command Line History
In the example below, the !^ next to the vi command gets the first argument from the previous command (i.e cp command) to the current command (i.e vi command).
# cp anaconda-ks.cfg anaconda-ks.cfg.bak anaconda-ks.cfg # vi !^ vi anaconda-ks.cfg
这个获取上个命令的第一个参数的方法太有用了,梦里寻他千百度,蓦然回首,那人却在灯火阑珊处!
如何判断一个命令是否为Linux的内建命令(即,如何获取Linux系统的bash的内建命令列表):
- http://www.gnu.org/software/bash/manual/bashref.html#Shell-Builtin-Commands
- site:stackoverflow.com linux builtin commands
- http://stackoverflow.com/questions/3192373/what-are-shell-built-in-commands-in-linux
- http://stackoverflow.com/questions/6103232/how-to-get-bash-built-in-commands-using-perl
- http://stackoverflow.com/questions/511683/bash-get-list-of-commands-starting-with-a-given-string
Linux系统查看命令是不是内建命令“type”
# type history history is a shell builtin
在解决问题的过程中浏览过的网页、搜索过的关键字真的是一个不可多得的经验财富!
@2014年6月23日 12:57:02