不要在shell提示符中使用重定向字符

2021-03-17 23:22:19

多年来,UNIX / Linux世界中的性能问题进行了故障排除问题,我已经看到了多个案例,在客户服务器中定期使用的命令行工具只需出于某种原因即可停止工作。该工具立即返回,绝对没有。没有印刷输出,没有COREDUMP和退出代码为零(成功!)。

这篇文章走过几个这样的事件,最后我解释了我如何避免意外地在生产中做错的东西。

以下是Linux服务器上此类问题的(手动再现)示例。 Expdp命令是Oracle数据库的高速数据导出工具,但这可能发生在任何文件中。通常是输出:

Oracle @ oel7l bin> pwd/u01/pap/app/oracle/product/18.0.0/dbhome_1/binoracle@oel7l bin> Oracle @ oel7l bin> Expdp Help = yexport:版本18.0.0.0 - MAR 16 17:55:35 2021Version 18.3.0.0.0copyright(c)1982,20.0.0,2018年,甲骨文和/或其附属公司。保留所有权利。数据泵出口实用程序提供了一种传输Oracle数据库的数据对象的机制。使用以下命令调用该实用程序:示例:Expdp scott / tiger目录= dmpdir dumpfile = scott.dmp ...删除了大量的输出...

但是,当客户在一个早晨试图在他们的环境中运行该命令时,会发生这种情况:

Expdp命令刚刚停止工作过夜!它立即返回,无所事事,但甚至没有错误消息,甚至是shell命令返回代码$?显示0 - 成功。

是时候挖掘了!让我们确保我们正在尝试在其正确的位置执行正确的命令:

到目前为止,所有看起来都是正确的,让我们看看二进制本身:

Oracle @ oel7l bin> File Expdp Expdp:Evervoracle @ Oel7l Bin> ls -l expdp -rwxr-x - x。 1 Oracle Oinstall 0 3月16日17:55 Expdp

繁荣!某些东西已将文件截断为零字节!我甚至看到了这个文件的最后一个修改时间,它可能会给一些额外的线索都有关于它可能已经/谁(有一些数据库软件修补/释放,然后由人手动完成)。

轻松快速检查是在该服务器中查看shell历史文件(如用户&#39中的.bash_history; home目录)。我将使用fc -l查询自己的用户历史记录:

Oracle @ oel7l bin> FC -L1044 RM LS1045 CD BIN / 1046 PWD1047 EXPDP帮助= Y1048 Oracle @ Oel7L Bin≫ PWD1049 /U01/PARP/ORACLE/Product/18.0.0/DBHOME_1/BIN1050 ORACLE @ OEL7L BIN> 1051 ORACLE @ OEL7L BIN> Expdp Help = Y1052出口:发布18.0.0.0 - 0.0.0 - Tue Mar 16 18:55:35 20211053版权(c)1982,201054 1982年,2018,甲骨文和/或其附属公司。版权所有.1055 EXPDP帮助= y1056哪个expdp1057文件expdp1058 ls -l expdp 1059 pwdoracle @ oel7l bin>

哇,突出显示的命令根本看起来不像贝壳命令一样!他们实际上看起来像有人意外地粘贴了从他们的终端屏幕上的一些随机垃圾回到壳牌中作为命令!

现在,所有这些突出显示的“命令”就会刚刚出错,因为它们是一个随机终端输出,而不是有效的shell命令,对吧?例如,尝试执行此命令将为您提供错误...

由于上述的“命令未找到”shell错误,但上面的命令本身并没有成功,但失败命令的输出(零字节)仍将写入“&gt”之后的任何文件名。重定向字符。现在,如果您碰巧在您的主目录或'/ tmp'中,您将意外地创建一个名为Expdp的新文件。但是,当您恰好在您的应用程序二进制目录(就像在这种情况下)或使用先前命令中的完整可执行路径时,最终将CLOBBOTing现有的二进制文件。您将截断它并用零字节文件替换它。从那时起,你的shell很高兴地执行零字节“shell”脚本,并返回任何成功。

从我的终端滚动回到这个实验的历史记录,你会看到我意外地从屏幕上粘贴了很多垃圾回归命令 - 而不是所有的都是无害的:

Oracle @ oel7l bin> Oracle @ oel7l bin> Oracle @ oel7l bin> Oracle @ oel7l bin> pwd-bash:Oracle @ oel7l:命令不是unitor @ oel7l bin> /u01/paracle/product/18.0.0/dbhome_1/bin-bash:/u01/app/oracle/product/18.0.0/dbhome_1/bin:是一个目录@ oel7l bin> Oracle @ oel7l bin> -bash:意外令牌的语法错误,意外令牌`newline' oracle @ oel7l bin> Oracle @ oel7l bin> -bash:意外令牌的语法错误,意外令牌`纽诺' Oracle @ oel7l bin> Oracle @ oel7l bin> Expdp Help = Y-Bash:Oracle @ oel7l:命令不是oledor @ oel7l bin> Oracle @ oel7l bin>导出:版本18.0.0.0 - 生产上周图3月16日17:55:35 2021-bash:出口::命令不是unitor @ oel7l bin>版本18.3.0.0.0-bash:版本:命令不是oder @ oel7l bin> Oracle @ oel7l bin>版权所有(c)1982年,2018年,甲骨文和/或其附属公司。版权所有。 - Bash:意外标记的语法错误,意外标记`C' Oracle @ Oel7L Bin>

在一个系统中,我曾经看过,有人在root中设法截断/箱子!它开始作为一个令人兴奋的“omg他们删除了整个文件系统?!"锻炼:

什么,根目录也消失了?如果root离开,我甚至如何登录?有人还删除了东西吗?我们被黑了吗?!

幸运的是,您不必只依赖于LS来列出文件和amp;目录名称。除了找到您可以使用shell的内置通配符扩展:

根@ oel7l bin> echo / * / bin / boot / dev / dev / etc / home / lib / lib64 / media / mnt / opt / proc / root /运行/ sbin / srv / sys / tmp / u01 / u02 / u03 / u04 / usr / varroot @ oel7l bin>

文件仍然存在!我们可以通过文件或stat命令进一步检查文件元数据(支持Glob通配符扩展):

根@ oel7l bin>文件//:目录号@ oel7l bin>根@ oel7l bin> stat / file:'/'尺寸:4096块:8 io块:4096目录设计:fc00h / 64512d inode:128链接:21access:(0555 / dr-xr-xr-x)uid:(0 / root)gid :( 0 / root)上下文:system_u:object_r:root_t:s0access:2021-03-16 19:14:49.953914433 -0400modify:2018-09-14 19:01:29.695056064 -0400Change:2018-09-14 19:01:29.695056064 -0400出生: -

我们甚至看到最后一个修改时间戳与stat命令(ls从与引擎盖下的stat与stat相同的地方获得其信息)。

所以,LS二进制文件有一些具体问题。运行的下一个逻辑命令是哪个ls。它会向您展示路径中的哪个目录,它找到了一个具有此类名称的文件,可以访问您并具有“x”位设置。它较少所知,但也显示了如果有人别名为恶作剧的命令将您的LS命令别名。

嗯,我一直认为LS不/ usr / bin。让我们检查/ bin目录是否只是一个symlink:

根@ oel7l bin>文件/ bin / bin:符号链接到`usr / bin' root @ oel7l bin>根@ oel7l bin> stat / bin文件:'/ bin' - > 'USR / BIN'尺寸:7个街区:0 IO块:4096符号链接器:FC00H / 64512D inode:773链接:1Access:(0777 / LRWXRWXRWX)UID:(0 / root)GID:(0 / root)上下文:system_u :Object_R:Bin_T:S0Access:2021-03-16 17:38:36.389289020 -0400Modify:2018-08-19 11:2018-0400Change:2018-08-19 11:29:23.860005567 -0400出生: -

文件和stat都可以告诉我们我们是否正在处理链接以及它指向的位置。使用echo *模式诀窍我可以看到/ usr / bin仍然存在其他文件:

根@ oel7l bin> Echo / USR / BIN / LS * / USR / BIN / LS / USR / BIN / LSATTR / USR / BIN / LSBLK / USR / BIN / LSCPU / USR / BIN / LSINTRD / USR / BIN / LSIPC / USR / BIN / LSLOCKS / USR / BIN / LSLOGINS / USR / BIN / LSMEM / USR / BIN / LSNS / USR / BIN / LSSCSIROT @ Oel7L BIN>文件/ usr / bin / ls / usr / bin / ls:空

根@ oel7l bin> stat / usr / bin / ls文件:'/ usr / bin / ls'尺寸:0块:0 IO块:4096常规空摘机:FC00H / 64512D inode:104356链接:1Access:(0755 / -RWXR-XR-X )UID:(0 / root)gid:(0 / root)上下文:system_u:object_r:bin_t:s0access:2021-03-16 17:57:58.944003009 -0400modify:2021-03-16 17:57:54.594064500 -0400Change :2021-03-16 17:57:54.594064500 -0400出生: -

该文件的最后修改日期可能会让我额外了解可能意外地覆盖文件的内容/谁。 .bash_history在终端屏幕中显示了类似的“某人”粘贴的模式:

根@ oel7l bin> FC -L1005 LS / VAR / RUN1006 LS / VAR / LOG1007 LS / VAR / LOCAL1008 ROOT @ OEL7L BIN> ls / var / local1009根@ oel7l bin> 1010 LS / VAR / LOCAL1111 LS / 1012其中LS1013 PWD1014 LS -LD / BINROOT @ OEL7L BIN>

您现在需要恢复LS二进制文件或从安装包重新安装它。或者,如果没有工作LS命令在进行还原操作的同时驱动螺母,则可以创建一个基于shell通配符扩展的临时shell脚本甚至是只使用echo * :-)的别名

根@ oel7l bin>别名ls = echoroot @ oel7l bin> LS / * / BIN / BOOT / DEV / ETC / HOME / LIB / LIB64 / MEDIC / MNT / OPT / PROC / ROOT / RUN / SBIN / SRV / SYS / TMP / U01 / U02 / U03 / U04 / U02 / U04 / USR / VAR

在Bash中,你可以拍摄-o noclobber!这将告诉shell使用重定向运算符来删除(覆盖内容)。

让我们来检查当前值,创建一个测试文件并启用NOCLOBBER,看看它是如何帮助的:

$ set -o | grep clobnoclobber off $ $ echo hello> $ $ cat ahello $ $ set -o noclobber $ set -o | Grep Clobnooblobber在$

好的! Bash不允许覆盖文件a。同样,在错误的终端输出中意外粘贴不会覆盖文件:

但是,如果您的目标是避免意外文件修改,则Noclobber选项不是非常简单的证明。例如,Bash允许您覆盖常规NOCLOBBER设置和使用> |要说你真的想打击该文件:

它不太可能与某人的提示以&gt结尾; |当您从终端中无意中粘贴到随机垃圾时,这些字符序列可能会发生。更多,Noclobber不会阻止其中一个且gt;&gt ;:

$ set -o noclobber $ cat a $ $ $ echo hello> a-bash:答:不能覆盖现有文件$ $ cat a $

据NocloBber,我们无法将现有的空文件覆盖与迄今为止的任何东西。但我们试着追加:

这需要更加异国情调的情况,意外执行的命令实际上必须实际存在并打印到其标准输出中,因为它要附加到&gt之后的任何文件名。>重定向。 Linux世界的一些例子将是MySQL或PCP用户(可执行命令名称==典型安装中的用户名,因此有些人可能会有他们的提示,看起来像MySQL>或PCP>登录为这些用户时。不过,粘贴从终端的终端中有一堆这种不幸的垃圾;>在它可能会导致你将随机的东西追加到现有的二进制文件和脚本,而不是将它们截断为零。(哪一个更好?🤔)

典型的根提示符中的#呢? #角色后的一切都会是评论,对吗?我允许Clobbering和Am使用的echo命令在这里保持更简单的东西:

上面的例子没有尝试覆盖我的文件,作为#> A被视为评论。上面重复的黄色“zzz”只是echo命令的标准输出,在终端屏幕上显示(并且由于评论#字符而对文件的重定向没有启动。我的文件的内容仍然说“你好”。

现在让我们做一个最后的微小变化,因为我希望我的令人敬畏的壳牌提示更紧凑。 “我刚修剪了一些空白,我认为我们不需要测试这一点”:

哦,垃圾,我应该测试过!对空格的微小更改改变了echo命令的含义。没有“zzz”和“#”之间的空间,shell认为它是传递给回声通讯的单个参数的一部分(echo zzz#),然后将该命令的输出重定向到我的文件a。

希望到现在它很明显,为我们的生产系统(和我们自己)的理智,您不应该使用>在shell提示。但是,这将无法保证避免与其他不良命令的意外粘贴有关的问题。例如,在所需的目录/文件名前缀和*在此示例中具有意外的空间 - 不要运行它!:

上面的命令将尝试删除单个文件/某些/ app / dir / dir oldlog_以及与当前工作目录中匹配的任何匹配的任何文件!

这个东西很复杂!小错误可以回来咬你的方式。我们毕竟正在运行关键的生产系统 - 如何可靠地避免这些问题,所以我们不会依赖于没有发生的人类错误,并且总是有运气?

我在这里没有解决任何更高级别的解决方案,如各种不可变基础设施 - 代码斑块 - 它们消除了大部分的“手动日常打字”人为错误风险,并将剩余的风险转移到不同的层。

如果您实际上需要手动登录服务器,那么我知道的最佳解决方案是在不需要时不使用特权访问。这将减少便利性,但会增加安全性。

不要以root或sudo登录到交互式根外壳,即使是开发机器 - 他们也是某人的生产!

在生产中,甚至不在操作系统级别登录数据库/应用程序所有(以便上面的格式错误的RM命令无法删除重要文件)

在生产中,您可以通过无密码sudo启用典型(诊断)命令(但其他所有其他需要密码)

在应用操作系统的变化时,将它们全部写入脚本,测试它并使用sudo +密码运行该精确脚本

sudo和/ etc / sudoers并不仅用于获得对root用户的选择性访问,但也可以是任何其他用户(DBA,APP WABLE)

这样,即使你在剪贴板中粘贴一些意外垃圾,你不能在不同的OS用户下弄乱一些东西,除非你是故意的,或者非常不幸。

回到本文中的原始主题 - 从计算机的剪贴板中粘贴到随机的东西很糟糕!

对我来说,避开一些粘贴恐怖的第一步是不要使用立即粘贴右鼠标右键的终端。我不小心右点击我的鼠标每天多次!我在过去的13年里使用了这个终端,因为它给了我水平滚动,没有疯狂的右键单击粘贴。我用刻意的cmd + v,没有别的

我使用各种Notes.txt样式文件。交互式在生产服务器中(具有实时性能故障排除乐趣!),我倾向于首先在单独的窗口中将任何非琐碎的命令写入编辑器(通常在类似测试环境中测试命令)

我不复制&粘贴命令。我通常会切断&粘贴回到同一个文本编辑器窗口,以确保最新命令肯定会放入剪贴板(我有很多浏览器或MS Word窗口的出现只是默默地忽略我的复制命令)。

一旦我相信剪贴板包含我的意图,我会立即将其粘贴到生产终端窗口。有时使用手动键入的#前缀只是为了在击中“go”之前仔细检查它

在额外的偏执模式中,即使访问任何浏览器窗口时,我也会仔细检查我的剪贴板内容(你知道,因为帕斯特杰克)

您可能会认为您只是通过将内容粘贴到生产服务器端的Vim编辑器,但如果剪贴板缓冲区包含一个^ Esc:q!\ nsomeverybadcommand \ n或不幸的vim宏?所以我倾向于削减&将剪贴板粘贴到我本地编辑器,立即将其粘贴到服务器之前

我希望这是一个娱乐读取......也许它有助于解释旧的神秘事件,当你必须只恢复备份时恢复mysql或sqlplus二进制文件,而其他一切似乎都很好(除了你的shell提示与&gt除外; 后缀 ;-)

此文件clobbering问题只是意外输入如何在您的服务器中混淆事物的一个示例,即使您没有击中一些最坏的情况rm -rf或shutdown命令也是如此。有多种可靠的选择,可避免麻烦和大大减少当地爆炸半径。具有良好的命令迅速卫生,特别是在重要的系统中,也减少了头痛的数量。之后,你需要良好的备份。

如果您想要进一步阅读 - 在我之前的博客条目中解释了一个探望行为行为壳二进制文件的方法。它讨论了通过使用strace系统调用跟踪的突然SSH登录延迟进行故障排除。虽然scrace不直接跟踪应用程序的内部用户空间逻辑,但在立即,突然的退出或类似于“我的配置文件更改之类的情况下,它仍然非常有用的是”我的配置文件更改“:

查看我的2021年在线培训课程! Linux性能和amp;故障排除培训,高级Oracle故障排除培训,高级Oracle SQL Tuning培训。除了实时在线课程,所有与会者也将收到个人可下载的视频录制!