自删除的Shell脚本

72

我在寻找解答这个问题的方法,但没有找到。

我写了一个简单的脚本来进行服务器的初始设置,并希望在完成后将其从根目录中删除/取消链接。我尝试了很多谷歌上找到的解决方案(例如/bin/rm $test.sh),但是脚本似乎总是留在原地。这种情况可能吗?以下是我目前的脚本。

#! /bin/bash
cd /root/
wget -r -nH -np --cut-dirs=1 http://myhost.com/install/scripts/
rm -f index.html* *.gif */index.html* */*.gif robots.txt
ls -al /root/

if [ -d /usr/local/psa ]
    then
        echo plesk > /root/bin/INST_SERVER_TYPE.txt
    chmod 775 /root/bin/*
    /root/bin/setting_server_ve.sh
    rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
    sed -i "75s/false/true/" /etc/permissions/jail.conf
        exit 1;
elif [ -d /var/webmin ]
    then
    echo webmin > /root/bin/INST_SERVER_TYPE.txt
    chmod 775 /root/bin/*
    /root/bin/setting_server_ve.sh
    rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
    sed -i "67s/false/true/" /etc/permissions/jail.conf
        break
    exit 1;
else
    echo no-gui > /root/bin/INST_SERVER_TYPE.txt
    chmod 775 /root/bin/*
    /root/bin/setting_server_ve.sh
    rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
    sed -i "67s/false/true/" /etc/permissions/jail.conf
        break
    exit 1;
fi  

4
如果你要使用一个自我删除的脚本,你必须确保在每次测试之前都有一个备份副本存放在其他地方。或者在测试时将 rm 替换为 ${RM:-/bin/rm}。你的代码将 rm 的输出传输到下一个 rm 命令中,这很奇怪;rm 不会读取标准输入(除非进行交互),也不会写入标准输出。这种重复是不好的。而且我绝不希望你删除我的机器上 /root 目录下的内容。此外,请检查 cd /root/ 是否有效;cd /root/ || exit 1 - Jonathan Leffler
1
嗨Jonathan。非常感谢你的反馈。正如你所看到的,我对这一切都很新。我在rm test.sh部分犯了一个非常基本的错误,但现在它已经正常工作了。还纠正了rm命令。再次感谢! - RFH
4
为什么你需要删除这个脚本?比如说,如果你只是在远程主机上运行一次,你可以直接运行ssh host < script.sh 而不需要复制它。请注意,此翻译为简洁性而设计,并保持原意,未做解释或修改。 - l0b0
@RFH:这不是对这个特定问题的答案,但也许可以帮助您以不同的方式解决问题。如果您正在尝试管理一组服务器的配置,则可能需要查看像puppet(http://puppetlabs.com/)、cfengine(cfengine.com)、salt(http://www.saltstack.com/)等适当的配置管理工具。这些工具不仅可以进行初始设置,而且还可以保证配置在之后保持正确。 - Bram
8个回答

114
rm -- "$0"

这应该能解决问题。$0是一个魔术变量,代表执行的脚本的完整路径。


3
bash下,似乎是这样的($0是脚本的路径);但并非总是如此(其他shell、其他操作系统),甚至现在可能也不是在每个系统上都是这样。 - Jonathan Leffler
2
在C语言层面上,你可以做任何事情:execl("/home/you/bin/script", "/usr/bin/turpentine", "-c", "pygmalion", (char *)0);,这使得你得到一个与实际命令名称无关的 argv [0],至少在传统的Unix系统中是这样。尽管bash似乎会提供它用于定位脚本的实际路径,但这是一个由bash决策而非POSIX规范指定的设计决策。传统上,在非Linux系统上,如果在命令行中键入“script”,则argv [0](又称脚本中的$0)只是“script”。据我所知,POSIX 2008未更改规则。 - Jonathan Leffler
23
不能以$0为完整路径来依赖它。如果你的脚本是通过"sh script-name"这样的方式调用的,那么$0将会是准确的"script-name"。这在http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html#中有说明。 - William Pursell
3
赞成解决可移植性问题的评论。此外,更多使用引号:rm -- "$0" - l0b0
5
注意!如果路径中有空格,rm 命令将尝试删除路径中第一个空格之前的部分,并将该路径项的第一部分(直到第一个空格为止)视为文件名。最好将 $0 用引号括起来,例如 rm "$0"。 - Scott Gardner
显示剩余2条评论

25

这对我有用:

#!/bin/sh

rm test.sh
也许你在 "$test.sh" 中不是真的想要保留 '$' 符号?

10
脚本在退出时可以通过shred命令(作为安全删除)自我删除。
#!/bin/bash

currentscript="$0"

# Function that is called when the script exits:
function finish {
    echo "Securely shredding ${currentscript}"; shred -u ${currentscript};
}

# Do your bashing here...

# When your script is finished, exit with a call to the function, "finish":
trap finish EXIT

2

最简单的方法:

#!/path/to/rm

使用方法:./path/to/the/script/above

注意: /path/to/rm 不能包含任何空格。


2
我写了一个小脚本,基于用户742030的答案https://dev59.com/uGox5IYBdhLWcg3ww3GV#34303677,给自删除脚本添加了优雅期。
function selfShred {
    SHREDDING_GRACE_SECONDS=${SHREDDING_GRACE_SECONDS:-5}
    if (( $SHREDDING_GRACE_SECONDS > 0 )); then
        echo -e "Shreding ${0} in $SHREDDING_GRACE_SECONDS seconds \e[1;31mCTRL-C TO KEEP FILE\e[0m"
        BOMB="●"
        FUZE='~'
        SPARK="\e[1;31m*\e[0m"
        SLEEP_LEFT=$SHREDDING_GRACE_SECONDS
        while (( $SLEEP_LEFT > 0 )); do
            LINE="$BOMB"
            for (( j=0; j < $SLEEP_LEFT - 1; j++ )); do
                LINE+="$FUZE"
            done
            LINE+="$SPARK"
            echo -en $LINE "\r"
            sleep 1
            (( SLEEP_LEFT-- ))
        done
    fi
    shred -u "${0}"
}

trap selfShred EXIT

在这里输入图片描述 点击此处查看存储库:https://github.com/reedHam/self-shred


1

$0在某些情况下可能不包含脚本的名称/路径。请查看以下内容:https://dev59.com/qVsW5IYBdhLWcg3wZmqc#35006505(选择$0和BASH_SOURCE之间的区别...)

在以下情况下,以下脚本应按预期工作:

  • source script.sh - 脚本被源化;
  • ./script.sh - 交互式执行;
  • /bin/bash -- script.sh - 作为shell程序的参数传递。
#!/usr/bin/env bash

# ...

rm --  "$( readlink -f -- "${BASH_SOURCE[0]:-$0}" 2> '/dev/null'; )";

请查看以下关于shell脚本源代码读取和执行的内容,因为当脚本在运行时被删除时,可能会影响其行为:https://unix.stackexchange.com/a/121025/133353Linux如何处理shell脚本?...)
相关: https://dev59.com/yXVD5IYBdhLWcg3wL4cA#246128如何从Bash脚本中获取源目录...)

0

只需添加到末尾:

rm -- "$0"

-10
为什么要完全删除脚本?正如其他人所提到的,这意味着您必须在其他地方保留副本。
一个建议是使用“第一次启动”的方法。只需在例如 /etc/sysconfig 中创建一个空文件,如果存在此脚本,则触发该脚本的执行。然后在脚本结束时删除该文件。
修改脚本,以便它具有必要的 chkconfig headers,并将其放置在 /etc/init.d/ 中,以便在每次启动时运行该脚本。
这样,您可以通过重新创建触发脚本来稍后重新运行脚本。
希望能对您有所帮助。

41
我通常不会点“踩”,但如果我这样做了,我会留下评论。所以在这里是:你的“答案”并不是被问到的问题的解决方案。而且通常问问题者想要做什么不关你的事。质疑提问者的动机是多余的、让人恼火和离题的。就像在这个特定的情况下:有时候一个文件删除自己是罕见但很重要的,比如当你想运行一个卸载脚本时。ziesemer的回答是在这种情况下完美的解决方案。 - Max
2
@TranSonHai:如果你对这个答案感到不满,我很抱歉。但是我真诚地认为,在许多情况下,这种方法对于面临类似问题的用户非常有用。 - Bram
8
我不知道任何情况下您的方法是有益的。我的理解是,当您想要卸载某些东西时,您希望在此之后不留下任何痕迹。创建一个外部脚本来删除东西最终会导致一个自相矛盾的情况,即您需要创建另一个外部脚本来删除该外部脚本,以此类推。那么为什么不接受上面提供的简单而优雅的答案,避免使事情变得不必要地复杂呢? - Max

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接