views:

57

answers:

4

Hello,

I was wondering if they was a way to prevent some commands from being executed in order to prevent from bad manipulation sometimes (for exemple you execute "rm *.py" when you wanted to execute "rm *.pyc" or something like that).

People will say that it's the user's responsability to check his inputs and it's right but I would like to know anyway if there's a way.

For "basic" things, we can use aliases in our bashrc like :

alias apt-get="echo 'We use aptitude here !'"
alias sl="echo 'Did you mean ls ?'"

But for something with arguments like "rm -f *.py" (or "rm -Rf /"), this simple trick don't work. Of course, I just want a basic method that prevent the exact command to be executed (same spaces and same arguments ordering would be a good start).

Thank you very much for your answers.

EDIT :

Here's a short python script based on the idea of simply wrapping command like "rm". Bash wrapper are maybe a better idea but I like python :) :

#!/usr/bin/python
# coding=UTF-8

import getopt, sys
import subprocess
import re

exprs=[]
exprs.append(re.compile(".*\.py$"));
exprs.append(re.compile(".*\.cpp$"));
exprs.append(re.compile(".*\.hpp$"));
exprs.append(re.compile("\*$"));


def main():
        try:
            opts, args = getopt.getopt(sys.argv[1:], "Rrfiv", ["interactive","no-preserve-root","preserve-root","recursive","verbose","help","version"])
        except getopt.GetoptError, err:
            # print help information and exit:
            print str(err)
            usage()
        optsString = "".join([opt[0]+opt[1]+" " for opt in opts]);
        for arg in args:
            tab = [expr.match(arg) for expr in exprs];
            if  tab.count(None)!=len(tab):
                 print "Not removing "+str(arg);
            else
                cmd = ["/bin/rm"]+[opt[i] for opt in opts for i in range(2) if not opt[i]=='']+[str(arg)];
                rmfile = subprocess.Popen(cmd)

if __name__=="__main__":
    main();
+3  A: 

Well, you can use the time-honoured approach of putting one of your own path components in front of all the others:

PATH=~/safebin:$PATH

and then, in ~/safebin, you put scripts that are "safer" like rm:

#!/bin/bash

for fspec in "$@" ; do
    if [[ "${fspec: -3}" = ".py" ]] ; then
        echo Not removing ${fspec}, use /bin/rm if you really want to.
    else
        echo Would /bin/rm "${fspec}" but for paranoia.
    fi
done

That script outputs for rm *:

Would /bin/rm chk.sh but for paranoia.
Would /bin/rm go but for paranoia.
Would /bin/rm go.sh but for paranoia.
Would /bin/rm images but for paranoia.
Would /bin/rm images_renamed but for paranoia.
Would /bin/rm infile.txt but for paranoia.
Would /bin/rm jonesforth.S but for paranoia.
Would /bin/rm jonesforth.f but for paranoia.
Would /bin/rm mycode.f but for paranoia.
Would /bin/rm num1.txt but for paranoia.
Would /bin/rm num2 but for paranoia.
Would /bin/rm num2.txt but for paranoia.
Would /bin/rm proc.pl but for paranoia.
Would /bin/rm qq but for paranoia.
Would /bin/rm qq.c but for paranoia.
Would /bin/rm qq.cpp but for paranoia.
Would /bin/rm qq.in but for paranoia.
Not removing qq.py, use /bin/rm if you really want to.
Would /bin/rm qq.rb but for paranoia.
Would /bin/rm qq.s but for paranoia.
Would /bin/rm qq1 but for paranoia.
Would /bin/rm qq2 but for paranoia.
Would /bin/rm qqq but for paranoia.
Would /bin/rm rm but for paranoia.
Would /bin/rm source.f90 but for paranoia.
Would /bin/rm test.txt but for paranoia.
Would /bin/rm xx but for paranoia.
Not removing xx.py, use /bin/rm if you really want to.

Now obviously the "${fspec: -3}" = ".py" is a simplistic one and a black list. I'd probably prefer to have a white list of things I was allowed to delete and deny everything else.


And here's a white list version based on regular expressions:

#!/bin/bash

for fspec in "$@" ; do
    del=0
    if [[ ! -z "$(echo "${fspec}" | grep 'a.e')" ]] ; then
        del=1
    fi
    if [[ ! -z "$(echo "${fspec}" | grep '\.[Ss]$')" ]] ; then
        del=1
    fi

    if [[ ${del} -ne 1 ]] ; then
        echo "Not removing ${fspec}, use /bin/rm if you want."
    else
        echo "    Removing ${fspec}"
        #/bin/rm "${fspec}
    fi
done

which outputs:

Not removing chk.sh, use /bin/rm if you want.
Not removing go, use /bin/rm if you want.
Not removing go.sh, use /bin/rm if you want.
    Removing images
    Removing images_renamed
Not removing infile.txt, use /bin/rm if you want.
    Removing jonesforth.S
Not removing jonesforth.f, use /bin/rm if you want.
Not removing mycode.f, use /bin/rm if you want.
Not removing num1.txt, use /bin/rm if you want.
Not removing num2, use /bin/rm if you want.
Not removing num2.txt, use /bin/rm if you want.
Not removing proc.pl, use /bin/rm if you want.
Not removing qq, use /bin/rm if you want.
Not removing qq.c, use /bin/rm if you want.
Not removing qq.cpp, use /bin/rm if you want.
Not removing qq.in, use /bin/rm if you want.
Not removing qq.py, use /bin/rm if you want.
Not removing qq.rb, use /bin/rm if you want.
    Removing qq.s
Not removing qq1, use /bin/rm if you want.
Not removing qq2, use /bin/rm if you want.
Not removing qqq, use /bin/rm if you want.
Not removing rm, use /bin/rm if you want.
Not removing source.f90, use /bin/rm if you want.
Not removing test.txt, use /bin/rm if you want.
Not removing xx, use /bin/rm if you want.
Not removing xx.py, use /bin/rm if you want.
paxdiablo
Thanks for your answer. It's maybe the best solution after all ! I might consider doing some scripting in python for more readability (and if I loose python, use directly /bin/rm) but your bash scripts seem to be well done !
Elenaher
+1  A: 

You might want to take a look at this http://code.google.com/p/safe-rm/

Rahul
+1  A: 

You may also use the lesser-known command "command" (which is similar to the "builtin" command) to create a restrictive rm wrapper:

help builtin command | less

# test example using ls instead of rm
function ls() {
   for ((i=1; i<=$#; i++ )); do
      arg="${@:i:1}"
      echo "arg ${i}: ${arg}"
      [[ "${arg}" == \*.py ]] && { echo "Not allowed: ${arg}"; return 1; }
   done
   command ls "${@}"
   return 0
}

ls -a

ls -a *.py
yabt
you might want to use `builtin command ls "${@}"` in case command is aliased or overwritten by a function
lesmana
A: 

Correction to my ls function example:

[[ "${arg}" != \*.py ]] && [[ "${arg}" == *.py ]] && \
   { echo "Not allowed: ${arg}"; return 1; }

Then:

ls -a *.py

# ... and also play with modified shell wildcard expansion behaviour ...
( shopt -s nullglob; ls -a *.py )
yabt