Batch rename spaces in filenames to underscore

127 views Asked by At

I have a directory called windows which contains files with names containing spaces. I want to replace the spaces in file names with underscores _. I need to correct all file names in the windows directory.

Constraints:

  • I'm allowed only one command.
  • Solutions must not contain control structures (if, while, for, etc.).
  • Solutions must not contain command substitutions $(...), special characters like ;, &, &&, ${}, ||, etc.

I'm looking for a solution that respects all the constraints mentioned above. Any help or guidance would be greatly appreciated. Thank you!

3

There are 3 answers

12
Barmar On

Use the built-in -regex condition to match the filenames, instead of piping to grep.

Use the built-in -exec operator to execute the bash command to perform the filename substitution and mv command.

Use sed in a command substitution to perform the space replacements.

find windows -type f -regex '/.{4} .{4} .{4}-[0-9A-Za-z]{1,3}$' -exec bash -c 'mv "$1" "$(sed "s/ /_/g" <<<"$1")"' {} {} \;
1
jhnc On

A comment clarifies that the filenames of interest follow a rigid naming convention:

all the files follow a specific pattern: four letters, a space, four letters, a space, four letters, a hyphen, and one to three digits

So:

find windows -maxdepth 1 -type f |
grep -iE '^windows([/ ][a-z]{4}){3}-[0-9]{1,3}$' |
xargs -r -n3 sh -c 'mv "$*" $1_$2_$3' --

Another comment states:

I can only use what's provided in a basic Ubuntu distribution

coreutils (mv), findutils (find/xargs), grep (grep) and dash (sh) are all required packages in Ubuntu.

base64 is also required, allowing:

echo ZmluZCB3aW5kb3dzIC1tYXhkZXB0aCAxIC10eXBlIGZ8Z3JlcCAtaUUgJ153aW5kb3dzKFsvIF1bYS16XXs0fSl7M30tWzAtOV17MSwzfSQnfHhhcmdzIC1yIC1uMyBzaCAtYyAnZWNobyBtdiAiJCoiICQxXyQyXyQzJyAt | base64 -d | sh
2
ikegami On
find windows -depth -exec rename -e 's/ /_/g' {} +

-depth if needed if any of the file names with spaces are directories.

This rename is provided as perl-rename on some distros.

Same idea, just without rename:

find windows -depth -exec perl -e'
   use File::Basename qw( fileparse );
   for ( @ARGV ) {
      my $old = $_;
      my ( $f, $d ) = fileparse( $_ );
      $f =~ s/ /_/g;
      my $new = $d.$f;
      rename( $old, $new ) if $new ne $old;
   }
' {} +

Same idea, but moves the loop from perl to find so you don't see for (but it does introduce a shell special character, \):

find windows -depth -exec perl -e'
   use File::Basename qw( fileparse );
   my $old = $ARGV[0];
   my ( $f, $d ) = fileparse( $old );
   $f =~ s/ /_/g;
   my $new = $d.$f;
   rename( $old, $new ) if $new ne $old;
' {} \;