I'd like to rename a group of files to their same filename but without extension. They are scattered in my home directory and I don't know where they are located precisely, but I do know the extension to be removed.

I need to have one and only one command to perform this task.

Here's the partial command:

$ find ~/ -type f -name "*.hhs" -exec mv {} <I DO NOT KNOW> \;

Where <I DO NOT KNOW> is to be replaced by your suggestion. For example, I tried to replace it with the following:

`basename {} .hhs`

It does not work for me.


If files file1.jpg.hhs and file2.jpg.hhs are found, I'd like them to be renamed as follows: file1.jpg and file2.jpg

  • 1
    why do you need only one command? I'm curious. – Jeff Schaller Nov 28 '18 at 14:17
  • Your find example uses cp , which would create a copy yet your title and body of the question imply a rename -- what's the goal? – Jeff Schaller Nov 28 '18 at 14:18
  • My apologies, I corrected the title. – Faxopita Nov 28 '18 at 14:28
  • The reason why I want everything under one command: for the beauty. I like to condense. Why creating a multiline script with variables while my task could be solved under one command only. – Faxopita Nov 28 '18 at 14:31

If you have or can install the PERL extension, rename (apt-get install rename on a Debian-based distribution)...

find ~ -type f -name "*.hhs" -exec rename -d ".hhs" {} +

The option, -d or --delete deletes the specified string.


You require a tool that can delete the .hhs filename suffix from the filename. find can not do this for you, so you will have to call another utility through -exec:

find "$HOME" -type f -name '*.hhs' -exec sh -c '
    for pathname do
        cp -i "$pathname" "${pathname%.hhs}"
    done' sh {} +

I'm not using basename here as that would also remove the directory path from the found pathnames (which could be inserted again with dirname, but it would yield messy code).

Instead, I call an in-line shell script with as many found pathnames as possible, and let the script loop over these, copying the individual files. The new name is constructed using a standard parameter substitution that deletes the string .hhs from the end of the value of $pathname.


  • @JeffSchaller Noted. – Kusalananda Nov 28 '18 at 14:40
  • +1 Great method, @Kusalananda :-) Please explain how the shellscript works in this case: how is pathname given its value? What is the difference compared to find . -name "*.pdf" -exec bash -c 'echo cp -p "$1" "${1%%.pdf}"' bash {} \; which works for me (I tested locally and for pdf files). – sudodus Nov 28 '18 at 15:14
  • @sudodus I hoped to avoid that explanation by linking to a more expressive general answer regarding the various uses of -exec with find. But in general, find passes the found pathnames to the utility that you call from -exec, replacing {} with the pathname that it has found. With -exec ... {} +, find will replace {} with not one pathname, but with as many as possible. This means that the script that I have is called with a number of pathnames, and the loop in the script loops over these. – Kusalananda Nov 28 '18 at 15:44
  • @sudodus In your example with bash, the bash shell will be invoked once for each file. This is slow if there are many files. – Kusalananda Nov 28 '18 at 15:45
  • Thanks. But one more question. I have learned that a shellscript will use $1 etc as parameters. How come you can use pathname for this purpose? Will the first available variable be used in these cases? – sudodus Nov 28 '18 at 15:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.