Delete spaces, hyphens, and underscores in filenames?

The version of rename that comes with the perl package supports regular expressions:

rename "s/[-_ ]//g" *

Alternatively,

rename -i "s/[-_ ]//g" *

The -i flag will make rename use interactive mode, prompting if the target already exists, instead of silently overwriting.

Perl's rename is sometimes called prename.

Perl's rename versus util-linux's rename

On Debian-like systems, perl's rename seems to be the default and the above commands should just work.

On some distributions, the rename utility from util-linux is the default. This utility is completely incompatible with Perl's rename.

  • All: First, check to see if Perl's rename is available under the name prename.

  • Debian: Perl's rename should be the default. It is also available as prename. The rename executable, though, is under the control of /etc/alternatives and thus could have been altered to something different.

  • archlinux: Run pacman -S perl-rename and the command is available as perl-rename. For a more convenient name, create an alias. (Hat tip: ChiseledAbs)

  • Mac OSX According to this answer, rename can be installed on OSX using homebrew via:

    brew install rename 
    
  • Direct Download: rename is also available from Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
    

I would replace all those tr commands, with a sed substitution command, e.g.:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done

Not counting mv, you don't really need an outside process for this at all - you can kind of just poof them.

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Still though, that means a mv invocation per file, and so probably rename is better. Though this should work given only a POSIX mv in $PATH and a POSIX shell.

So, I came up with a kind of crazy demo for this. The test set is generated like:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

In the first place I will be the first to acknowledge that the above command produces results that can be more easily obtained by other means. But other means would likely not demonstrate as well what might be done with $IFS and a little (sick?) imagination.

So the first bit is fairly straight-forward:

  • tee pipes out 5 copies of its input - the heredocument called CGEN

  • dd blocks its input by newlines at 90 bytes per block and pipes that to ...

  • sed joins 2 of those blocks on two \newline characters, 'single-quotes the results, and prepends the string touch -- for every line cycle before piping out to ...

  • sh which then executes all input as shell commands

The #CGEN bit though... Well, briefly...

  • the bottom printf prints 252 0s

  • the next from last receives 252 '' null-string arguments and for each prints the contents of $n followed by the string " $i "

  • eval interprets the arguments of the next up printf before it prints the results of that interpretation as octal nums prepended by 2 backslashes a piece

  • the last printf prints the byte values for those octals 2 at a time followed by the string -_ ---___ for each pair

  • $n is initialized to an equation that will increment $i by one for every evaluation excepting that it skips the values 10, 39, or 47 - (which are \newline, 'single-quote, and /slash in ASCII decimal respectively)

The end result is a directory containing a lot of really ugly filenames containing every byte in my charset from 1 through 255 excepting the single-quote (only skipped to avoid one more sed s/// statement) and the /slash. Those filenames look like this:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Now I'll get some data on these files:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

OUTPUT

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

Ok. Now finally, to action:

ifsqz '_ -'
chksqz '_ -'

OUTPUT

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

Success! You can see for yourself:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff