5

I want to delete folders using regexp in a Mac terminal.

0129_0140 (no delete)
0140_0140 (delete)
0150_0160 (no delete)
0170_0170 (delete)

I just want to delete folders such as 0140_0140 , 0170_0170 .

(Added) I want to delete the nonempty folders, recursively.

jakeoung
  • 161

4 Answers4

12

Non-recursive

With ksh93 (on OS/X available as ksh):

rmdir {4}(\d)_\1

(beware it could delete a directory called {4}(\d)_\1 if there's no file matching that pattern).

With zsh (on OS/X available as zsh):

setopt extendedglob
rmdir [0-9](#c4)_[0-9]##(/e:'[[ ${REPLY%_*} = ${REPLY#*_} ]]':)

(that one also has the benefit of only considering files of type directory, using the / glob qualifier above).

With bash or other POSIX shell (like the sh of most systems including OS/X):

set -- [0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]
for f do
  [ "${f#*_}" = "${f%_*}" ] && set -- "$@" "$f"
  shift
done
rmdir "$@"

(beware it could delete a directory called [0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9] if there are no XXXX_XXXX files in the current directory).

Using find and grep:

find . ! -name . -prune -type d -name '[0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]' |
  grep -x '\./\(.*\)_\1' |
  xargs rmdir

With BSD find (as found on OS/X):

find . -maxdepth 1 -regex './\([0-9]\{4\}\)_\1' -type d -delete

With GNU find (as typically not found on OS/X unless installed via macports/homebrew/fink...):

find . -maxdepth 1 -regextype grep -regex './\([0-9]\{4\}\)_\1' -type d -delete

Recursively:

  • ksh93:

    set -o globstar
    rmdir -- **/{4}(\d)\1
    

    (beware that it won't remove 1111_1111 in case there's a 1111_1111/2222_2222 as it will try to remove the 1111_1111 one first which it can't as there's a 2222_2222 dir in it, ksh93 doesn't have the od glob qualifier (for depth-first order) of zsh)

  • zsh:

    setopt extendedglob
    rmdir -- **/[0-9](#c4)_[0-9]##(Dod/e@'[[ ${${REPLY:t}%_*} = ${REPLY##*_} ]]'@)
    
  • BSD find:

    LC_ALL=C find . -regex '.*/\([0-9]\{4\}\)_\1' -type d -delete
    
  • GNU find:

    LC_ALL=C find . -regextype grep -regex '.*/\([0-9]\{4\}\)_\1' -type d -delete
    
  • The command doesn't work for me. I also tried find . -regex '{4}([0-9])_\1', but it also failed. Could you explain about the command? – jakeoung Feb 10 '16 at 10:22
  • Doesn't osx use bash (As default anyway) ? – 123 Feb 10 '16 at 10:24
  • @jakeoung, shells are command line interpreters, the application that are usually started within terminal emulators like OS/X Terminal. By default terminal emulators usually start your login shell which by default on OS/X I believe is something like tcsh or bash. To start another shell, just enter its name at a shell prompt. zsh or ksh for instance. OS/X El Capitan at least has all of bash, zsh and ksh93 pre-installed. – Stéphane Chazelas Feb 10 '16 at 10:45
  • Using find and grep works in default mode. Oh, I see. Many thanks. – jakeoung Feb 10 '16 at 10:46
  • But it doesn't work recursively. find . ! -name . -prune -type d -name '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' | grep -x './(.*)\1' | xargs rm -rf – jakeoung Feb 10 '16 at 10:51
  • 1
    @jakeoung, well you never said it had to be recursive, the -prune is to explicitely tell it not to recurse. Please edit your answer to add the information, also specify whether you want to delete the directory contents as well (if those directories may not be empty), and if dirs like 0001_0001/0002_0002 may exist. – Stéphane Chazelas Feb 10 '16 at 11:02
  • @StéphaneChazelas Won't the directory's contents be deleted automatically when the top level directory is deleted ? – 123 Feb 10 '16 at 11:08
  • @123, not with rmdir which only removes directories, not their contents. – Stéphane Chazelas Feb 10 '16 at 11:18
  • @StéphaneChazelas Where do the contents go then ? – 123 Feb 10 '16 at 11:22
  • 1
    @123, directories that are not empty cannot be removed obviously. See my comment for ksh93 in recursively – Stéphane Chazelas Feb 10 '16 at 11:46
  • @StéphaneChazelas Yep just realised that, thanks :) – 123 Feb 10 '16 at 11:47
  • You forgot to mention nullglob. – Scott - Слава Україні Feb 10 '16 at 18:19
2

POSIX (remove echo from echo rmdir to actually erase the files) :

for dir in [0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]/; do
    a="$(expr "$dir" : '\(.*\)_\1/')"
    ${a:+false} || echo rmdir "$dir"
done

Or:

for d in [0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]/; do
    expr "$d" : '\(\(.*\)_\2\)/' >/dev/null && echo rmdir "$d"
done

Or:

set -- [0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]/
for    d
do     expr "$d" : '\(\(.*\)_\2\)/' >/dev/null && echo rmdir "$d"
done
1

Try this:

find -type d -regextype posix-extended -regex '\./([0-9]{4})_\1' -delete

regex type setting is needed for the {4}.

ntki
  • 161
  • Thats GNU find. – 123 Feb 10 '16 at 10:33
  • 1
    Note that POSIX extended regexps don't have backreferences, so that command might not work on all systems where GNU find is available if it's configured to use the system regexp library. Use posix-basic instead which has backreferences (also note that \{4\} was added to BRE before {4} was added to EREs (and broke backward portability when that happened which explains why the default regexps in GNU find don't support it for instance)). – Stéphane Chazelas Feb 10 '16 at 10:54
  • A basic-regex is: find . -regextype posix-basic -regex '\./\([0-9]\{4\}\)_\1' –  Feb 10 '16 at 11:30
1

A pair of simple, POSIX-compliant alternatives:

Non-recursive (newlines disallowed in basename):

ls -F | grep '^\([0-9]\{4\}\)_\1/$' | xargs rm -fr

Recursive:

find . -type d -exec expr {} : '.*/\([0-9]\{4\}\)_\1$' \; -prune \
       -exec rm -fr {} + >/dev/null
Barefoot IO
  • 1,946