15

I have the following files in the following format:

$ ls CombinedReports_LLL-*'('*.csv
CombinedReports_LLL-20140211144020(Untitled_1).csv
CombinedReports_LLL-20140211144020(Untitled_11).csv
CombinedReports_LLL-20140211144020(Untitled_110).csv
CombinedReports_LLL-20140211144020(Untitled_111).csv
CombinedReports_LLL-20140211144020(Untitled_12).csv
CombinedReports_LLL-20140211144020(Untitled_13).csv
CombinedReports_LLL-20140211144020(Untitled_14).csv
CombinedReports_LLL-20140211144020(Untitled_15).csv
CombinedReports_LLL-20140211144020(Untitled_16).csv
CombinedReports_LLL-20140211144020(Untitled_17).csv
CombinedReports_LLL-20140211144020(Untitled_18).csv
CombinedReports_LLL-20140211144020(Untitled_19).csv

I would like this part removed:
20140211144020 (this is the timestamp the reports were run so this will vary)

and end up with something like:

CombinedReports_LLL-(Untitled_1).csv
CombinedReports_LLL-(Untitled_11).csv
CombinedReports_LLL-(Untitled_110).csv
CombinedReports_LLL-(Untitled_111).csv
CombinedReports_LLL-(Untitled_12).csv
CombinedReports_LLL-(Untitled_13).csv
CombinedReports_LLL-(Untitled_14).csv
CombinedReports_LLL-(Untitled_15).csv
CombinedReports_LLL-(Untitled_16).csv
CombinedReports_LLL-(Untitled_17).csv
CombinedReports_LLL-(Untitled_18).csv
CombinedReports_LLL-(Untitled_19).csv

I was thinking simply along the lines of the mv command, maybe something like this:

$ ls CombinedReports_LLL-*'('*.csv

but maybe a sed command or other would be better

Community
  • 1
  • 1
HattrickNZ
  • 4,373
  • 15
  • 54
  • 98
  • Possible duplicate of [removing a part of filename of a bunch of files](http://stackoverflow.com/questions/12174947/removing-a-part-of-filename-of-a-bunch-of-files). – Joel Feb 11 '14 at 02:57

5 Answers5

20

rename is part of the perl package. It renames files according to perl-style regular expressions. To remove the dates from your file names:

rename 's/[0-9]{14}//' CombinedReports_LLL-*.csv

If rename is not available, sed+shell can be used:

for fname in Combined*.csv ; do mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')" ; done

The above loops over each of your files. For each file, it performs a mv command: mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')" where, in this case, sed is able to use the same regular expression as the rename command above. s/[0-9]{14}// tells sed to look for 14 digits in a row and replace them with an empty string.

IRTFM
  • 258,963
  • 21
  • 364
  • 487
John1024
  • 109,961
  • 14
  • 137
  • 171
  • 1
    No need for sed -- a modern shell can do its own substitutions built-in. – Charles Duffy Feb 11 '14 at 03:11
  • @CharlesDuffy (1) I generally aim for Dash-compatibility not just because it is so much faster than bash but also because plain shell is much more widely supported. (2) while Bash can do primitive substitutions, sed's regex's are ultimately much more powerful. – John1024 Feb 11 '14 at 04:05
  • The PEs available in POSIX sh, and thus dash, are sufficient for this particular job. Granted, you can't do replacements, but you **can** filter for beginning and ending strings and combine them. As for the claim that sed's regexes are more powerful -- bash has built-in regex support as well (see `BASH_REMATCH`), and has a mode in which its globs are comparable in power to BREs (see `extglob`). – Charles Duffy Feb 11 '14 at 13:12
  • ...the big difference, though, is efficiency; if you were iterating over hundreds of files, the time penalty of forking a subshell, exec'ing sed within it, wait()ing for that subshell to complete, read()ing its output, etc. adds up quickly. – Charles Duffy Feb 11 '14 at 13:13
  • ...also, there are side effects to using command substitution -- any time you use `$(foo)`, trailing characters in `IFS` are stripped, so a filename ending with whitespace would have that whitespace stripped, even if that's not the behavior you wanted. – Charles Duffy Feb 11 '14 at 13:15
  • 1
    `rename` might works a little bit different than described by @John1024: `rename from to files`. And with the same examples: `rename 20140211144020 '' CombinedReports_LLL-*'('*.csv` – user890739 Aug 08 '16 at 16:44
  • @user890739 _"rename might works a little bit different than described"_ You are referring to a _different_ `rename`. The `rename` discussed in this answer, as mentioned in the first sentence of the answer, is the version that comes with the _perl_ package. The perl version is the default on debian-like systems. You may be describing the version that comes as part of the `util-linux` package. – John1024 Aug 09 '16 at 05:16
9

Without using an other tools like rename or sed and sticking strictly to bash alone:

for f in CombinedReports_LLL-*.csv
do
  newName=${f/LLL-*\(/LLL-(}
  mv -i "$f" "$newName"
done
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • +1. But why not put all in mv command directly? `mv -i "$f" "${f/LLL-*\(/LLL-(}"` – BMW Feb 11 '14 at 03:02
  • 3
    Why? For clarity of course. Look into the hearts and minds of the fellow programmer and guess which solution will be understood best. My guess was, by the way OP put his question, that he would understand it better with a speaking variable name like `newName`. – Alfe Feb 11 '14 at 03:04
  • Is that a standard regex? Or some bash-specific kind? I can't find any docs on a bash-specific regex language but don't understand why `LLL` is repeated or the second opening bracket. – Chris Jan 27 '17 at 12:43
  • It is not a regexp. It is more or less a glob-pattern replacement. See `man bash` and search for `Pattern substitution`. – Alfe Jan 27 '17 at 12:48
2
for f in CombinedReports_LLL-* ; do
    b=${f:0:20}${f:34:500}
    mv "$f" "$b"
done

You can try line by line on shell:

f="CombinedReports_LLL-20140211144020(Untitled_11).csv"
b=${f:0:20}${f:34:500}
echo $b
Pedro Nunes
  • 410
  • 2
  • 5
  • 1
    Let's hope all numbers (the timestamps) are of the same width. And still, anybody reading that code will have to guess where those constants (`0`, `20`, `34`, and `500`) have come from and what they mean. It's not impossible to guess of course, but it's not the clearest way to put it either. – Alfe Feb 11 '14 at 03:10
0

You can use the rename utility for this. It uses syntax much like sed to change filenames. The following example (from the rename man-page) shows how to remove the trailing '.bak' extension from a list of backup files in the local directory:

rename 's/\.bak$//' *.bak
Kevin
  • 2,112
  • 14
  • 15
0

I'm using the advice given in the top response and have put the following line into a shell script:

ls *.nii | xargs rename 's/[f_]{2}//' f_0*.nii

In terminal, this line works perfectly, but in my script it will not execute and reads * as a literal part of the file name.

cjhveal
  • 5,668
  • 2
  • 28
  • 38