找不到命令时的安全shell重定向

时间:2014-07-24 11:08:16

标签: bash shell

当我运行命令时(在Ubuntu 14.04中,bash版本4.3.11),我们说当前目录中有一个名为text的文本文件(并不重要) ):

nocommand > text # make sure noommand doesn't exists in your system

它会报告未找到的'命令'错误,它会删除文本文件!我只是想知道如果命令不存在我是否可以避免文件的破坏。 我尝试这个命令set -o noclobber但是如果我运行会发生同样的问题:

nocommand >| text # make sure noommand doesn't exists in your system

似乎bash在查找要运行的特定命令之前重定向输出。谁能给我一些建议如何避免这种情况?

5 个答案:

答案 0 :(得分:4)

实际上,shell首先查看重定向并创建文件。之后它会评估命令。

因此,究竟发生了什么:因为它是>重定向,它首先用空文件替换文件,然后评估一个不存在的命令,这会在stderr上产生错误消息而什么都没有在stdout上。然后它将stdout存储在这个文件中(这没什么,所以文件保持为空)。

我同意Nitesh您只需要先检查命令是否存在,但根据this thread,您应该避免使用which。我认为一个好的起点是在脚本的开头检查你可以运行所有必需的函数(参见线程,3个解决方案),否则就中止脚本。

答案 1 :(得分:3)

仅当管道发送至少一个字符时才会写入文件:

nocommand | (
    IFS= read -d '' -n 1 || exit
    exec >myfile
    [[ -n $REPLY ]] && echo -n "$REPLY" || printf '\x00'
    exec cat
) 

或使用功能:

function protected_write {
    IFS= read -d '' -n 1 || exit
    exec >"$1"
    [[ -n $REPLY ]] && echo -n "$REPLY" || printf '\x00'
    exec cat
}

nocommand | protected_write myfile

请注意,如果启用了lastpipe选项,则必须将其放在子shell中:

nocommand | ( protected_write myfile )

根据您的选择,您也可以默认在函数上召唤子shell:

function protected_write {
    (
        IFS= read -d '' -n 1 || exit
        exec >"$1"
        [[ -n $REPLY ]] && echo -n "$REPLY" || printf '\x00'
        exec cat
    )
}
  • ()召唤一个子贝壳。子shell是一个fork,在不同的进程空间上运行。在x | y中,默认情况下会在子shell中召唤y,除非lastpipe选项(try shopt lastpipe)已启用。
  • IFS= read -d '' -n 1等待一个字符(请参阅help read)并在读取一个绕过exit的字符时返回零代码。
  • exec >"$1"stdout重定向到文件。这使得打印到stdout的所有内容都打印到文件中。
  • 读取后\x00以外的所有内容都存储在REPLY中,这就是printf '\x00' {null}(空)值时REPLY的原因。
  • exec catcat替换子shell的进程,该进程会将收到的所有内容发送到文件并完成剩余的作业。请参阅help exec

答案 2 :(得分:3)

首先写入临时文件,只有在命令成功时才将其移动到所需文件上。

nocommand > tmp.txt && mv tmp.txt text

这不仅避免了nocommand不存在时的错误,而且还避免了现有命令在完成写入输出之前退出时的错误,因此您不会用不完整的数据覆盖text。 / p>

如果有更多工作,您可以在发生错误时清理临时文件。

{  nocommand > tmp.txt || { rm tmp.txt; false; } } && mv tmp.txt text

内部命令组确保外部命令组的退出状态为非零,这样即使rm成功,也不会触发mv命令。

一个更简单的命令,当nocommand成功但mv失败时,会带来轻微的删除临时文件的风险

nocommand > tmp.txt && mv tmp.txt text || rm tmp.txt

答案 3 :(得分:1)

如果你这样做:

set -o noclobber

然后

invalidcmd > myfile

如果当前路径中存在myfile,那么您将获得:

-bash: myfile: cannot overwrite existing file

答案 4 :(得分:0)

使用"检查"命令

#!/usr/bin/env bash

command_name="npm2" # Add your command here
command=`which $command_name`

if [ -z "$command" ]; then #if command exists go ahead with your logic
        echo "Command not found"
else # Else fallback
        echo "$command"
fi

希望这有帮助