41

I would like to add text to the end of filename but before the extension. Right now I am trying,

for f in *.shp; do echo $f_poly; done

the output is,

Quercus_acutifolia.shp_poly
Quercus_agrifolia.shp_poly
Quercus_corrugata.shp_poly
Quercus_cortesii.shp_poly
Quercus_costaricensis.shp_poly
Quercus_havardii.shp_poly
Quercus_hemisphaerica.shp_poly
Quercus_kelloggii.shp_poly
Quercus_knoblochii.shp_poly
Quercus_laceyi.shp_poly

I want it to be,

Quercus_acutifolia_poly.shp
Quercus_agrifolia_poly.shp
Quercus_corrugata_poly.shp
Quercus_cortesii_poly.shp
Quercus_costaricensis_poly.shp
Quercus_havardii_poly.shp
Quercus_hemisphaerica_poly.shp
Quercus_kelloggii_poly.shp
Quercus_knoblochii_poly.shp
Quercus_laceyi_poly.shp
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Sam007
  • 513
  • 1
  • 4
  • 7

6 Answers6

43

Using standard POSIX parameter expansion:

for f in *.shp; do printf '%s\n' "${f%.shp}_poly.shp"; done
jw013
  • 50,274
  • 9
  • 137
  • 141
  • Awesome that is exactly what I needed. – Sam007 Nov 26 '12 at 21:05
  • 2
    Might be better with an explanation how it works. The Doug answer is pretty easy, on the other hand. – Display Name Jun 23 '15 at 12:41
  • @SargeBorsch What do you need explained? My answer, the snippet in the question, and Doug's answer are only differ by a few characters, and Doug's answer explains even less than mine so I don't know what it is you want. If you just compare the difference in the two outputs in the question it should be trivially easy to figure out what they do. I can explain why my answer is preferable to Doug's. 1. I use `printf` with a format string instead of the less portable `echo`. 2. I use parameter expansion which is more efficient than calling an external binary (`basename`) for such a simple task. – jw013 Jun 23 '15 at 14:43
  • 2
    Then the command to rename the files would be this: `for f in *.shp; do mv $f ${f%.shp}_poly.shp; done` – Patch92 Feb 13 '19 at 12:28
  • 1
    Is `printf` required here? If not, using it instead of the `echo` makes the answer more confusing than it needs to be. The little gained in portability (`echo` is ubiquitious nowadays) is lost in comprehensibility for novices. – Hashim Aziz Nov 27 '20 at 05:49
  • @Prometheus Here's the [long answer](https://unix.stackexchange.com/q/65803/9537). Short answer, yes printf is better. If I replaced it with echo I couldn't guarantee that it would work correctly on every machine with something named echo. – jw013 Nov 27 '20 at 22:36
8

Sometimes there is a tool called "rename" installed.

rename 's/\.shp$/_poly.shp/' *shp

It might not be portable but it is easy to use.

Chad Clark
  • 316
  • 1
  • 4
7

As the question is for bash there is no need for external utilities, since you can use bash regexps:

for f in *.shp
do
    mv -v "${f}" "${f%.*}_MYSUFFIX.${f##*.}"
done

❗️Warning: for f in *.ext is not reliable as it will break on file names containing spaces, quotes or other reserved characters. A failsafe approach would be using something like find . -iname '*.shp' -exec sh -c 'mv -v "${1}" "${1%.*}_MYSUFFIX.${f##*.}"' _ {} \;. If you don't need recursive traversal then add -maxdepth 1

ccpizza
  • 1,655
  • 1
  • 19
  • 19
  • 1
    I like this one the most since it works generically for any file extension. Thanks! – Hans Sep 02 '21 at 06:59
  • This is the best answer. Short, precise and compliant. No subshells. Works with any extension (`for i in *.{yml,yaml}; do ...`). Nitpicking: 1. Use `f`. `i` is no numerical index. 2. The question is about `echo`, not `mv`. – wedi Jan 19 '22 at 18:44
5

Use this:

for file in *.shp; do echo $(basename $file .shp)_poly.shp; done
Michael Durrant
  • 41,213
  • 69
  • 165
  • 232
doneal24
  • 4,910
  • 2
  • 16
  • 33
  • 4
    Using `basename` is slower and less efficient than letting the shell do the work by itself. This may be noticeable for very large numbers of files. – jw013 Nov 26 '12 at 21:14
  • 1
    Also, there are missing quotes and `--`s and it fails for filenames that have newline characters before the `.shp`. – Stéphane Chazelas Nov 16 '16 at 22:57
  • 1
    Thanks for giving an answer I can actually work with for my purpose. This is bash help, not code golf. (reading over this.. I realize it may have sounded like I was being sarcastic. More gripping about the other answers on here than yours. Thanks again) – Tim Jul 04 '19 at 15:48
3

This worked better for me:

for f in *; do NEW=${f%.webm}_2016.webm; mv ${f} "${NEW}"; done

Vinnie James
  • 131
  • 3
  • Well this looks a lot like the accepted answer except that you probably want `for f in *.webm`, you forgot to quote the `${f}` and you're missing a `--`. – Stéphane Chazelas Nov 16 '16 at 22:45
  • 1
    The accepted answer doesnt work on OSX, it only prints out the new file names, it doesnt actually rename the files – Vinnie James Nov 16 '16 at 22:47
  • 1
    Of course, it shows you how to use shell expansions to get the new file name, in response to the question that is also outputing a file name (with `echo`), but not the required one. – Stéphane Chazelas Nov 16 '16 at 22:54
1

If they are in different locations then run :-

for i in ` find /root/test/ -name "*.shp" ` ;
do
  mv $i ` echo $i | sed 's/.shp$/_poly.shp/g' ` ;
done
X Tian
  • 10,413
  • 2
  • 33
  • 48