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

4 Answers 4

15

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

remove the -n when happy.

8
  • 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, 2019 at 7:01
  • 2
    perl-rename in arch linux
    – Lesmana
    May 14, 2019 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!
    – dandev486
    May 14, 2019 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, 2019 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, 2019 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'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

References:

Looping through command output in bash

Substrings in bash

4
  • 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, 2019 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?
    – dandev486
    May 15, 2019 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, 2019 at 0:42
  • 2
    I see, it actually matters if the intent is renaming a lot of files. Thank you for the clarification!
    – dandev486
    May 15, 2019 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.

3
  • The usual implementations of a pipe will have forks.
    – muru
    May 17, 2019 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.
    – user339730
    May 17, 2019 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, 2019 at 5:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .