72

I've looked around for an answer to this one but couldn't find one.

I have written a simple script that does initial server settings and I'd like it to remove/unlink itself from the root directory on completion. I've tried a number of solutions i googled ( for example /bin/rm $test.sh) but the script always seems to remain in place. Is this possible? Below is my script so far.

#! /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  
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
RFH
  • 723
  • 1
  • 5
  • 5
  • 3
    If you're going to play with a self-deleting script, you have to make sure you have a backup copy somewhere every time before you test it. Or you can replace the `rm` with `${RM:-/bin/rm}` for testing. Your code piping `rm` outputs into the next `rm` command is pretty weird; `rm` neither reads from standard input unless it goes interactive (which it won't with `-rf`), nor writes to standard output. The repetition is bad. And there's no way I want you removing stuff from `/root` on my machines. Also, check that `cd /root/` works; `cd /root/ || exit 1`. – Jonathan Leffler Jan 24 '12 at 01:59
  • 1
    Hi Jonathan. Thanks a lot for your feedback. As you can tell, I am pretty new to all this. I was making a really basic mistake with the placement of the rm test.sh part but it works fine now. Also corrected the rm command. Thanks again! – RFH Jan 24 '12 at 03:19
  • 4
    Why do you need to delete this script? For example, if it's a one-time thing you run on remote hosts, you can run `ssh host < script.sh` without copying it at all. – l0b0 Apr 12 '12 at 12:56
  • @RFH: this is not an answer to this specific question but can perhaps help you solve the problem in a different way. If you're trying to manage the configuration of a set of servers you may want to look into a proper configuration management tool like puppet (http://puppetlabs.com/), cfengine (cfengine.com), salt (http://www.saltstack.com/), etc. Those tools not only can do the initial setup but they can guarantee that the configuration remains correct afterwards. – Bram Apr 03 '14 at 07:45

8 Answers8

110
rm -- "$0"

Ought to do the trick. $0 is a magic variable for the full path of the executed script.

richo
  • 8,717
  • 3
  • 29
  • 47
  • 3
    Under `bash`, that seems to be the case (`$0` is the path to the script); 'twas not always thus (other shells, other operating systems), and probably isn't so on every system even now. – Jonathan Leffler Jan 24 '12 at 02:33
  • I was under the impression that it was specified by posix, in saying that I'm no expert. I (ab)use kludges like `cd $(dirname $0)` pretty wildly in scripts that use `#!/bin/sh` as the interpreter on most of the mainstream BSD's, linux, osx and solaris and get away with it, but I can't make promises about everything. – richo Jan 24 '12 at 02:56
  • 2
    At the C level, there is nothing to stop you doing: `execl("/home/you/bin/script", "/usr/bin/turpentine", "-c", "pygmalion", (char *)0);` which gives you an `argv[0]` unrelated to the actual command name, at least in traditional Unix. Although `bash` seems to supply the actual path that it uses to locate the script, that is AFAIK a design decision by `bash` and not anything specified by POSIX. Conventionally, and in years past on non-Linux systems, if you typed "script" on the command line, then `argv[0]` (aka `$0` in the script) was just "script". AFAICT, POSIX 2008 did not change the rules. – Jonathan Leffler Jan 24 '12 at 03:26
  • 21
    You cannot rely on $0 being a full path. If your script is invoked as "sh script-name", then $0 will be exactly "script-name". (This is specified in http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html# ) – William Pursell Jan 24 '12 at 18:41
  • 3
    +1 for portability issue comments. Also, Use More Quotes: `rm -- "$0"` – l0b0 Apr 12 '12 at 12:49
  • 5
    Be careful with this! If there are spaces in the path, rm will attempt to remove the path up to the first space, treating the first portion of that path item (up to the first space) as a file name. Better to wrap the $0 in quotes, e.g., rm "$0" – Scott Gardner Sep 15 '13 at 15:43
  • 1
    In addition to @WilliamPursell, `source script.sh` may turn `$0` to the shell name (e.g. `bash`), so it may be better to use `${BASH_SOURCE[0]}` instead. Related: https://stackoverflow.com/a/35006505/5581893 – Artfaith May 22 '22 at 23:13
25

This works for me:

#!/bin/sh

rm test.sh

Maybe you didn't really mean to have the '$' in '$test.sh'?

ziesemer
  • 27,712
  • 8
  • 86
  • 94
10

The script can delete itself via the shred command (as a secure deletion) when it exits.

#!/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

The simplest one:

#!/path/to/rm

Usage: ./path/to/the/script/above

Note: /path/to/rm must not have blank characters at all.

2

I wrote a small script that adds a grace period to a self deleting script based on
user742030's answer https://stackoverflow.com/a/34303677/10772577.

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

enter image description here See the repo here: https://github.com/reedHam/self-shred

Reed Hambrook
  • 144
  • 2
  • 6
1

$0 may not contain the script's name/path in certain circumstances. Please check the following: https://stackoverflow.com/a/35006505/5113030 (Choosing between $0 and BASH_SOURCE...)

The following script should work as expected in these cases:

  • source script.sh - the script is sourced;
  • ./script.sh - executed interactively;
  • /bin/bash -- script.sh - passed as an argument to a shell program.
#!/usr/bin/env bash

# ...

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

Please check the following regarding shell script source reading and execution since it may affect the behavior when a script is deleted while running: https://unix.stackexchange.com/a/121025/133353 (How Does Linux deal with shell scripts?...)


Related: https://stackoverflow.com/a/246128/5113030 (How can I get the source directory of a Bash script from...)

Artfaith
  • 1,183
  • 4
  • 19
  • 29
0

Just add to the end:

rm -- "$0"
Shōgun8
  • 482
  • 10
  • 20
-10

Why remove the script at all? As other have mentioned it means you have to keep a copy elsewhere.

A suggestion is to use a "firstboot" like approach. Simply create an empty file in e.g. /etc/sysconfig that triggers the execution of this script if it is present. Then remove that file at the end of the script.

Modify the script so it has the necessary chkconfig headers and place it in /etc/init.d/ so it is run at every boot.

That way you can rerun the script at a later time simply by recreating the trigger script.

Hope this helps.

Bram
  • 819
  • 1
  • 9
  • 24
  • 40
    I don't usually thumb down but when I do, I leave a comment. So here it is: You "answer" is not a solution to what being asked. And generally it's none of your business why the asker wants to do something. Questioning the motive of the asker is redundant, irritating and off-topic. As in this particular case: it's rare but sometimes crucial for a file to delete itself, e.g. when you want to run an uninstall script. ziesemer's answer is a perfect solution in this case. – Max Apr 02 '14 at 04:06
  • 2
    @TranSonHai: Sorry if you're irritated by the answer. But I honestly believe that in many cases this approach is very useful for users facing a similar problem. – Bram Apr 03 '14 at 07:36
  • 8
    I don't know of any cases where your approach could be beneficial. My understanding is that when you want to, say have something uninstall itself, you want to leave no traces afterward. Creating an external script to delete things eventually leads to a paradoxical situation where you need to create another external script to delete that external script, and so on. So why not accept the simple, elegant answer that the people above have provided and quit making things unnecessarily complicated? – Max Apr 04 '14 at 21:08