Question

I just stumbled upon Perl today while playing around with Bash scripting. When I tried to remove blank spaces in multiple file names, I found this post, which helped me a lot.

After a lot of struggling, I finally understand the rename and substitution commands and their syntax. I wanted to try to replace all "_(x)" at the end of file names with "x", due to duplicate files. But when I try to do it myself, it just does not seem to work. I have three questions with the following code:

  • Why is nothing executed when I run it?
  • I used redirection to show me the success note as an error, so I know what happened. What did I do wrong about that?
  • After a lot of research, I still do not entirely understand file descriptors and redirection in Bash as well as the syntax for the substitute function in Perl. Can somebody give give me a link for a good tutorial?
find -name "*_(*)." -type f | \
  rename 's/)././g' && \
find -name "*_(*." -type f | \
  rename 's/_(//g'  2>&1
Was it helpful?

Solution

You either need to use xargs or you need to use find's ability to execute commands:

find -name "*_(*)." -type f | xargs rename 's/)././g'
find -name "*_(*."  -type f | xargs rename 's/_(//g'

Or:

find -name "*_(*)." -type f -exec rename 's/)././g' {} +
find -name "*_(*."  -type f -exec rename 's/_(//g'  {} +

In both cases, the file names are added to the command line of rename. As it was, rename would have to read its standard input to discover the file names — and it doesn't.

Does the first find find the files you want? Is the dot at the end of the pattern needed? Do the regexes do what you expect? OK, let's debug some of those too.

You could do it all in one command with a more complex regex:

find . -name "*_(*)" -type f -exec rename 's/_\((\d+)\)$/$1/' {} +

The find pattern is corrected to lose the requirement of a trailing .. If the _(x) is inserted before the extension, then you'd need "*_(*).*" as the pattern for find (and you'll need to revise the Perl regexes).

The Perl substitute needs dissection:

  • The \( matches an open parenthesis.
  • The ( starts a capture group.
  • The \d+ looks for 'one or more digits'.
  • The ) stops the capture group. It is the first and only, so it is given the number 1.
  • The \) matches a close parenthesis.
  • The $ matches the end of the file name.
  • The $1 in the replacement puts the value of capture group 1 into the replacement text.

In your code, the 2>&1 sent the error messages from the second rename command to standard output instead of standard error. That really doesn't help much here.

You need two separate tutorials; you are not going to find one tutorial that covers I/O redirection in Bash and regular expressions in Perl.

The 'official' Perl regular expression tutorial is:

  • perlretut, also available as perldoc perlretut on your machine.

The Bash manual covers I/O redirection, but it is somewhat terse:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top