12

I have directory say /var/tmp/abc which has 4 files:

12345-ram-3e3r5-io9490-89adu9.csv
45434-dam-qwe35-to9490-43adu9.csv
11234-cam-yy3r5-ro9490-85adu9.csv
14423-sam-hh3r5-uo9490-869du9.csv

I want to rename all the CSV files (find all the files & rename them) in shortest possible (probably one-liner) way to this:

XXXXX-ram-3e3r5-io9490-89adu9.csv
XXXXX-dam-qwe35-to9490-43adu9.csv
XXXXX-cam-yy3r5-ro9490-85adu9.csv
XXXXX-sam-hh3r5-uo9490-869du9.csv
15

rename -n 's/(\w+)/XXXXX/' *.csv

remove the -n when happy.

  • 2
    For a suitable version of rename. I assume this is Larry Wall's rename, from the rename package in Debian and derivative (and IIRC prename on systems of the RedHat persuasion). A very useful tool. – xenoid May 14 at 7:01
  • 2
    perl-rename in arch linux – lesmana May 14 at 10:44
  • 1
    @SHawarden, I've tried to execute the command and read the relevant portion of man page to understand the replace pattern, but when adding the verbose flag, it outputs nothing and doesn't perform the file name changes, even though the current path has files with names similar to 12345_foo.csv. Do I have to perform any additional task to get it working (currently on Fedora 30)? It is a cleaner approach than looping. Thanks in advance! – danieldeveloper001 May 14 at 11:23
  • 3
    @danieldeveloper001 Did you use rename or prename? See my comment above. man {the command} lists the authors at the end. – xenoid May 14 at 11:39
  • 2
    This only works on Debian-derived systems. Fedora-derived systems have a completely different rename command; see unix.stackexchange.com/a/238862/135943 – Wildcard May 15 at 6:02
11

Try:

for f in *.csv; do mv -i -- "$f" "XXXXX-${f#*-}"; done

How it works:

  • for f in *.csv; do

    This starts a loop over all *.csv files.

  • mv -i -- "$f" "XXXXX-${f#*-}"

    This renames the files as you want, asking interactively before overwriting any file.

  • done

    This marks the end of the loop.

Example:

$ ls -1
11234-cam-yy3r5-ro9490-85adu9.csv
12345-ram-3e3r5-io9490-89adu9.csv
14423-sam-hh3r5-uo9490-869du9.csv
45434-dam-qwe35-to9490-43adu9.csv
$ for f in *.csv; do mv -i -- "$f" "XXXXX-${f#*-}"; done
$ ls -1
XXXXX-cam-yy3r5-ro9490-85adu9.csv
XXXXX-dam-qwe35-to9490-43adu9.csv
XXXXX-ram-3e3r5-io9490-89adu9.csv
XXXXX-sam-hh3r5-uo9490-869du9.csv
4

I liked the little challenge that you've posted, so here is my solution. I'm assuming that all your files starts with 5 numeric characters, so using the cut command to replace the initial numeric files by "XXXXX".

Below, the files before the command.

-rw-rw-r--. 1 daniel daniel 0 May 13 23:18 11111_bar_file.csv
-rw-rw-r--. 1 daniel daniel 0 May 13 22:54 12345_baz_file.csv
-rw-rw-r--. 1 daniel daniel 0 May 13 22:54 67890_foo_file.xml

Below, the one liner command.

for src in *.csv; do dst=XXXXX$(echo $src| cut -c6-); mv $src $dst; done;

Below, the files after the command.

-rw-rw-r--. 1 daniel daniel 0 May 13 22:54 67890_foo_file.xml
-rw-rw-r--. 1 daniel daniel 0 May 13 22:54 XXXXX_bar_file.csv
-rw-rw-r--. 1 daniel daniel 0 May 13 23:18 XXXXX_baz_file.csv

Is that what you're looking for? :)

References:

Looping through command output in bash

Substrings in bash

  • 2
    Bash can do this sort of string manipulation, no need to fork a new process for each, please the other answer on how. – chx May 14 at 23:45
  • 1
    @chx, already saw the other answers and acquired a little bit of knowledge from it, but thanks for pointing them out. Would you please elaborate on no need to fork a new process for each? – danieldeveloper001 May 15 at 0:19
  • 3
    cut is a separate binary (/usr/bin/cut) and running it consumes more resources than using shell built ins. – chx May 15 at 0:42
  • 2
    I see, it actually matters if the intent is renaming a lot of files. Thank you for the clarification! – danieldeveloper001 May 15 at 0:55
1

no forks:

ls | perl -lne '$suf=substr($_,6); rename $_, "XXXXX-$suf"'

When you use a shell loop, the mv forks once per file. Perl's rename command does not.

(Perl's rename command has some restrictions, but in this specific case those restrictions don't apply.)

As for the rename command shown earlier, yes that works, but then you have all that confusion between two different kinds of rename and so on. If you have the right one, great, but if not, this works too.

If you don't have the perl-rename command and can't install it, you can just do this:

ls | perl -lne '$old=$_; s/(\w+)/XXXXX/; rename $old, $_'

As you can see, this uses the same substitution shown in the top answer. Of course the perl-rename has other bells and whistles (the top answer mentioned, -n already, then there's -0, -f, and so on), and the more of them you need, the more you should install that instead of rolling your own in this manner.

  • The usual implementations of a pipe will have forks. – muru May 17 at 5:24
  • I meant, "won't fork once per file to be renamed", unlike even shell, where the "mv" forks /usr/bin/mv or whatever. This is because the "rename" command is a perl internal command that directly calls rename() in libc. Of course it has some restrictions, but in this specific example those restrictions don't apply. – sitaram May 17 at 5:31
  • 1
    That's also true of the rename command (top answer, also uses Perl), so maybe you should add a little more explanation than simply "no forks" to the answer. – muru May 17 at 5:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.