Lets say I have a directory with the following files:

1.txt 2.txt 3.txt

I want to copy these and basically paste in the same directory making new files eg:

1.txt 1(1).txt 2.txt 2(1).txt 3.txt 3(1).txt

This is harder than I thought! This seems trivial on Windows, just highlight a bunch of files, copy/paste, and it creates the same files but adds 'Copy' into the name.

  • The Windows trick also works on some file browsers like Nautilus and Nemo. – muru Feb 4 '15 at 8:48

In plain Unix style you’d need to loop over the files:

for file in 1.txt 2.txt 3.txt; do cp -pi "$file" "${file%.*}(1).txt"; done

${file%.*} removes the extension from the filename, then (1).txt adds the number and restores the extension. The options to cp prompt if a target already exists (-i) and preserve permissions (-p).

Most Linux systems will have the mmv package available; that allows you to simply do

mcp '*.txt' '#1(1).txt'

This copies every file ending in .txt to a new file, matching the first wildcard (#1) and appending (1).txt. The parentheses don’t need escaping here because they’re within quotes.

Alternatively, if you use a graphical file manager, you can do exactly the same as you would in Windows.

| improve this answer | |
  • It would make sense to check first whether the target file name already exists (e.g. if the code is run twice accidentally). – Hauke Laging Feb 4 '15 at 6:44

This seems trivial on Windows, just highlight a bunch of files

I thing it is trivial in various GUI file managers in Linux too, but you need shell solution, right? Here it is:

for F in *; do cp -p "$F" "${F%.*}\(1\).${F#*.}"; done

${F%.*} extracts part of filename before dot, while ${F#*.} extracts extension.

| improve this answer | |
  • ${F#*.} will fail if there is more than one dot in the file name. – Hauke Laging Feb 4 '15 at 6:41
set ./*.txt
while    [ -e "$1" ] || [ -L "$1" ]
do       case ${1##*(} in ($1|*[!0-9]*\)|\)*)
         cp "$1" "${1%.*}(1).txt";;
         (*\)*) set "${1##*(}" "$@"; cp \
         "$2" "${2%"$1"}$((${1%)*}+1))).txt"
         shift;; esac
shift;   done

That should handle all cases pretty well, I think. It will increment the (copy) number if necessary - but will otherwise just insert a (1) between the extension and the file name.

| improve this answer | |

In zsh, wildcard move or copy is shipped with the shell:

autoload zmv
zmv -C '(*).txt' '$1 (1).txt'

But I would really advise against using a space in a filename for this purpose, and parentheses are also not ideal. I mention this because every time I encounter a pattern like this (for instance x.txt and x (1).txt and x (2).txt) and want to do tab completion without double quotes, I have to write x <tab> <backslash> <space> <backslash> ( 1 <tab> to get it right. Also, it looks confusing in ls output because it's not really clear where the filenames end. I'd use x.txt, x.1.txt and x.2.txt. This way, the filenames visually apear as single units, and this completion works: x <tab> .1 <tab>.

| improve this answer | |

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.