15

I want to rename files by removing the last N characters

For example I want to rename these files by removing the last 7 characters

From:

file.txt.123456

To:

file.txt

Is this doable in a single command?

heinistic
  • 731
  • 2
  • 8
  • 16

5 Answers5

12

You can remove a fixed number of characters using

mv "$file" "${file%???????}"  # 7 question marks to match 7 characters

This will work in any POSIX-compliant shell.

To remove the last extension (which may be more or less than 7 characters), use

mv "$file" "${file%.*}"

To trim everything after a given extension, you can try

EXT=csv
mv "$file" "${file%.$EXT.*}".$EXT

which actually removes .$EXT and everything after, but then reattaches .$EXT.

chepner
  • 497,756
  • 71
  • 530
  • 681
8

Are you using bash?

file="file.txt.123456"
mv $file ${file::(-7)}
Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • This has worked for me numerous times before. Now all of a sudden I am getting the error: -bash: (-7): substring expression < 0. Any ideas?? – KNN Apr 23 '20 at 15:17
  • Your string is too short, and you're asking bash to remove more characters than it has. :) – Adam Liss Apr 25 '20 at 13:39
  • But my file names are extremely long? Similar to this: ```T591_Name_Anothername_L200.fasta.new.new```. I'm using this in a loop as I've done a million times in the past. ```for file in *.new; do; mv $file ${file::(-7)}; done.```I'm baffled. – KNN Apr 27 '20 at 18:09
3

This is similar to Adam's, but without bashisms (since it was tagged shell not bash).

remove_n(){
  echo ${2:0:$((${#2}-$1))}
}

remove_n 8 file.txt.1234567
#remove last 8 characters from 2nd argument
#for filenames in variables use
mv "$VAR" `remove_n 8 "$VAR"`
technosaurus
  • 7,676
  • 1
  • 30
  • 52
  • 1
    `${foo:offset:length}` is the same bashism used by Adam. – chepner Oct 23 '13 at 19:57
  • it is also supported by other shells though, for instance busybox ash, whereas the other method isn't – technosaurus Oct 23 '13 at 20:38
  • I don't follow. The only difference I see is that Adam uses a negative length, while you compute an explicit positive length. – chepner Oct 23 '13 at 20:55
  • ::() is different than :${start}:${length} and `()` is not supported, but $((integer ops)) are nor is a negative value for substring manipulation, so start from position :0 (the zero is not required, but left in place to show intent) and go the length of the string ${#varname} minus the amount we need to take off which has to be done in $(()) outside of bash. I use busybox ash a lot, so the differences stick out. – technosaurus Oct 23 '13 at 21:16
  • OK, so it appears that `ash` and `bash` have two slightly different implementations of the same non-standard construct. Good to know. – chepner Oct 24 '13 at 12:21
  • @chepner - no, bash has additional implementations. both ways should work in bash, but this way will work in bash _AND_ other shells (maybe not dash or posix sh, but most others) – technosaurus Oct 25 '13 at 19:37
2

To do so for every file in directory you can use this.

As in first answer 7 question marks to match 7 characters

for file in *; do mv "$file" "${file%???????}"; done
vozman
  • 1,198
  • 1
  • 14
  • 19
0

Depending on what you mean by a single command, at least you can do it with some pipes:

mv file.txt.123456 $(ls file.txt.123456 | rev | cut -c8- | rev)

Looking at your example, I'm wondering if a pattern like "removing characters from the last dot to the end" would be a better fit.

Benjamin Toueg
  • 10,511
  • 7
  • 48
  • 79