3

Actually this kind of too much to ask I think.

But I want to remove all files matching the extension ".sh"(current folder only) which I can do with below command:

find . -maxdepth 1 -type f -name '*.sh' -exec rm {} +

But still I want to keep file "cron.sh" in the current folder unaffected.

How can I achieve this in single command line ?

terdon
  • 100,812
Vicky Dev
  • 433

2 Answers2

5

You can use -not:

find . -maxdepth 1 -type f -name '*.sh' -not -name cron.sh -delete

Also, GNU find has the -delete option which is simpler to use and than rm -rf.

Important: Always put -delete at the end. As explained in man find:

Warnings: Don't forget that the find command line is evaluated as an expression, so putting -delete first will make find try to delete everything below the starting points you specified.


Alternatively, you could use a shell loop (but don't, the find command above is far more efficient):

for f in *.sh; do [ "$f" = "cron.sh" ] || rm "$f"; done
terdon
  • 100,812
  • Thank you, please keep both the answers. So I can use -name and -not -name in same line, any disadvantages with them ? – Vicky Dev Oct 20 '16 at 12:18
  • @VickyDev um, no. What kind of disadvantages? You can do very complicated things with find. You can string multiple conditions together (-not, -or, -and) and use \( and \) for more complex grouping. This is very simple and one line should be fine. – terdon Oct 20 '16 at 12:21
  • Maybe you could point out that find's command execution is "left to right" so when using -delete to make sure to put it at the end. People have suffered from this often enough. Oh, the terrible nightmares are haunting me to this day.... – FelixJN Oct 20 '16 at 12:21
  • I rarely the gnu one, but if you are going the gnu find way, can you add " +" after "-delete" to have it work on multiple files at once (with less forking)? or is it not possible with -delete? – Olivier Dulac Oct 20 '16 at 12:42
  • @OlivierDulac no, at least on GNU find, the + only makes sense with -exec and -execdir. The -delete command doesn't take any arguments. As for being GNU find, yes, that's the only implementation that's on topic here. This is [ubuntu.se], after all. – terdon Oct 20 '16 at 14:02
  • With *.sh and bash, GLOBIGNORE is far more convenient, IMO. – muru Oct 20 '16 at 14:05
  • 1
    @OlivierDulac I just tested this by comparing the times of -delete, -exec rm {} \; and -exec rm {} + and the -delete was the fastest. I am assuming it calls unlink() directly and doesn't fork at all since it isn't calling any external commands. – terdon Oct 20 '16 at 14:07
  • @muru well, GLOBIGNORE="cron.sh" rm *.sh doesn't work (on my system, anyway). It deletes cron.sh as well. So you'd need to first run GLOBIGNORE="cron.sh" and then rm *.sh. And that does work, but leaves GLOBIGNORE set for the session. I prefer find. – terdon Oct 20 '16 at 14:10
  • Well, yes: GLOBIGNORE="cron.sh"; rm *.sh, and as for the variable being kept alive: (GLOBIGNORE="cron.sh"; rm *.sh). – muru Oct 20 '16 at 14:11
  • @muru ah, yes, using a subshell is a good idea. Why not post it as an answer? – terdon Oct 20 '16 at 14:13
  • @terdon I closed it as a dupe sometime ago. :D – muru Oct 20 '16 at 14:13
  • @terdon: it makes sense (that it unlinks directly), and indeed that makes it faster (no forking, and find already has the info "at hand" for the unlink). Thanks for looking it up – Olivier Dulac Oct 20 '16 at 14:20
  • @muru so I see. Good call. – terdon Oct 20 '16 at 14:50
1

An alternative, but slower way then the great find example by terdon is to use mv and rm commands chained with and operators.

mv cron.sh cron.sh.safe && rm *.sh && mv cron.sh.safe cron.sh

This will complain if you have any directories named something.sh, but will not delete them.

Arronical
  • 19,893