2

I accidentally copied the folders in my Music directory to the parent michal directory. I want to delete these directories in one fell swoop.

Is there any way to make the following work? Or is there a better alternative?

/home/michal/Music $ find . -type d -maxdepth 1 -exec rm -r ../{} \;

Evidently the {} is not being substituted, because the above returns

rm: ../{}: No such file or directory
rm: ../{}: No such file or directory
rm: ../{}: No such file or directory
...
Michal Z.
  • 31
  • 1
  • What about (sloppy pseudocode) for f in *; if [ -d "$f" ] then rm -r ../"$f" ; fi; done would something like that be acceptable? – Kalvin Lee Sep 27 '16 at 01:32
  • 1
    @KalvinLee Or, still simpler: for f in */; do rm -r ../"$f" ; done – John1024 Sep 27 '16 at 01:34
  • 1
    @John1024 marvelous. I'll be stealing that. – Kalvin Lee Sep 27 '16 at 01:41
  • @KalvinLee Go ahead and steal. – John1024 Sep 27 '16 at 01:49
  • Thanks, that does the job. But this issue with find is bothering me. I don't like needing special commands like this for special cases. This problem totally seems like one find should be able to solve. – Michal Z. Sep 27 '16 at 01:58
  • 1
    @MichalZ. What OS are you on? Your command works fine for me on Linux with GNU find. – John1024 Sep 27 '16 at 02:15
  • As you commented: running the command in android doesn't work. I am flagging this as "an error that went away on fixing a typo". –  Sep 27 '16 at 02:52
  • 1
    I believe that this doesn’t qualify for the “problem that went away when a typo was fixed” close reason. I suspect that you misunderstand Michal’s comment. You seem to think that he’s saying, “D’oh!  The command didn’t work because I ran it on the wrong platform; I ran it on a computer that didn’t have the folders I was trying to delete.” I believe the situation is that the command that fails on Android works on the workstation because the Android implementation of find (link to busybox) is less robust than GNU find, and doesn’t accept all the commands that GNU find accepts. … (Cont’d) – G-Man Says 'Reinstate Monica' Sep 27 '16 at 06:30
  • 1
    (Cont’d) … And it’s not a simple question of whether -exec cmd {} \; works; it’s a question of whether -exec cmd ../{} \; works. – G-Man Says 'Reinstate Monica' Sep 27 '16 at 06:30
  • Both -exec rm -r ../{} \; as busybox find … -exec busybox rm -r ../{} \; work. As long as there is a directory to erase. Not a problem of some busybox limitation IMO. –  Oct 23 '16 at 19:24

2 Answers2

3

Your find command works fine for me with GNU find. If it doesn't work on your system, try:

find . -maxdepth 1 -type d -exec sh -c 'rm -r "../$1"' Rm {} \;

This approach uses {} as an independent argument, not modified by ../. We pass the value of {} to the shell and the shell applies the ../.

Notes:

  1. The order of -maxdepth 1 and -type d was reversed as man find recommends that global options, like -maxdepth, be defined before positional options like -type.

  2. The shell command must be in single-quotes: 'rm -r "../$1"'. If it were in double-quotes, the originating shell would substitute its value for $1, and the directory name found by find would be ignored. We need the called sh to do the substitution. The string "../$1" is in double-quotes to protect the directory name, $1, against word splitting and pathname expansion.

  3. The shell assigns the string Rm to $0. This is unused unless there is an error message. Consequently, any string could be used.

John1024
  • 74,655
  • I recommend using something informative for the $0, such as find-rm which is descriptive of what you are running. Otherwise, perfect answer. – Wildcard Sep 27 '16 at 02:24
  • 1
    Wow, you're right, it does work on my desktop. I was attempting to run this command on my Android. Evidently busybox isn't as sophisticated with its parsing. Thanks a lot for providing me with the alternative! – Michal Z. Sep 27 '16 at 02:40
  • @MichalZ. The busybox find does accept CMD {} (except that find fails if the CMD fails). The command was failing because you executed it inside Android, and in there, the files you were trying to erase didn't exist. –  Sep 27 '16 at 05:31
  • @John1024: I understand that -maxdepth 1 can eliminate the need to stat things — and I know that, because it is an option (rather than a test), putting it at the beginning of the expression is recommended. But how is the order relevant? First of all, the options affect all tests, including tests specified before the option. And secondly, the -type d test forces you to stat every directory entry in . anyway. – G-Man Says 'Reinstate Monica' Sep 27 '16 at 06:29
0

With zsh:

cd /home/michal &&
rm -rf -- Music/*(/D:t)

The / glob qualifier is to select only directories, D to include hidden ones, and the :t modifier like in history expansion is to select only the tail (the file name without its directory part).

With any Bourne-like shell:

cd /home/michal/Music &&
set -- */ &&
cd .. &&
rm -rf -- "$@"

One difference with the above though is that it will also include symlinks to directories (and would remove the content of target instead of the link itself).