2

I want to write a script to find all the files ending with .cpp and rename them as .cc. The search will be done recursively from a start route R, which is the script's argument.

If no argument is written, R will be the actual directory.

And I have written:

#!/bin/bash

R=.
if [[ $# == 1 ]]
then
    echo "$# == 1"
    if [[ -d $1 ]]
    echo "$1 is directory"
    then
        R=$1
    else
        printf "Error $1 should be a directory"
        exit 1
    fi
find $R -name "*.cpp" -exec sh -c 'mv {} $(dirname {})${$(basename {})%.cpp}".cc" ' \;
exit 0
else
    printf "Invocation is: $0 directory"
    exit 1
fi

But I know I have trouble in the find line, because I don't know how to express I want to delete the extension and append the new one when using {}

Yone
  • 219

1 Answers1

6

Don't do such thing by operating directly on {}.

You have used inline sh, so pass {} as argument to it, and also you don't need to use basename at all:

find "$R" -name "*.cpp" -type f -exec sh -c '
  for f do
    mv -- "$f" "${f%.*}.cc"
  done
' sh {} +

Note that you have to double quote $R, otherwise, it lead to security vulnerable.

cuonglm
  • 153,898
  • Thank you cuonglm, thank you you have helped me. I have learnt that sh -c means commands' arguments are pass into the srting fully-quoted with ''. I have learnt it is important to partially quote all variables because of splitting and globbing. Two doubts: Why did you add -- next to the mv command? I have tried and without them it works equal. Also Why do I need to put sh {} + after the string which feeds the first sh -c with args, it is harsh to understand this aproach. – Yone Nov 02 '16 at 19:10
  • @enoy: The -- marks the end of command options, actually, it isn't necessary in this case, but I put there for general. the first argument passed to inline sh will be the named of inline sh process. Try something sh -c 'does not exist' foo and sh -c'does not exist' bar. – cuonglm Nov 03 '16 at 02:26