71

Is there a clever way to do copy and move operations or a command to duplicate a file, without having to do a cd, then mv after, at the same folder?

For example, I have to run the following:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

Note that the directory to where I'm moving the file is the same, but I have to put the whole path again and sometimes it gets annoying. I'm curious to know if there's another way to do that without having to put the whole path again, because the operation would be done in the same path.

5
  • 9
    I can't believe this has so many upvotes. It's a duplicate unix.stackexchange.com/questions/35782/… and unix.stackexchange.com/questions/66889/…
    – user13107
    May 17, 2013 at 16:28
  • 13
    @user13107 There are many ways to ask a question, including different wording. And if you don't know that the answer is called "brace expansion", you might not be able to find it right away.
    – slhck
    May 17, 2013 at 16:37
  • 2
    @user13107 they are on a different site so not duplicates
    – mmmmmm
    May 18, 2013 at 7:13
  • 1
    Mark, Thanks, I didn't know that rule about duplicates. @slhck Yes. I understand. I was just frustrated because my question on Unix.SE got closed as duplicate and this one got so popular.
    – user13107
    May 18, 2013 at 8:24
  • 3
    @user13107, that's what you get for posting on the right site May 18, 2013 at 14:08

6 Answers 6

123

Simply use brace expansion:

mv /folder1/folder2/folder3/{file.txt,file-2013.txt}

This is equivalent to writing:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

Brace expansion lets you supply more arguments, of course. You can even pass ranges to it, e.g. to create a couple of test folders, you can run mkdir test_{a..z}, and starting with Bash 4, you can create zero-padded sequences as well, as in touch foo{0001..3}, which creates foo0001, foo0002 and foo0003. The Bash Hackers Wiki has an article with a couple of examples for you.

If you have to use two different commands, use a subshell and cd there first, as in @Ignacio's answer.

7
  • 5
    I didn't know about the brace expansion, thank you! May 17, 2013 at 12:49
  • I tried and it seems not to work: meniac ~: mv /tmp/f1/f2/f3/f4/f5/f6/{file.txt, file2.txt} mv: cannot stat ``/tmp/f1/f2/f3/f4/f5/f6/{file.txt,': No such file or directory May 17, 2013 at 12:54
  • 5
    Are you sure you're using Bash, as in /bin/bash, and you're not in a script that has /bin/sh in the shebang or some other shell that doesn't support brace expansion? If you run set, do your SHELLOPTS contain braceexpand?
    – slhck
    May 17, 2013 at 12:57
  • 22
    Note that there should be no space between file.txt, and file2.txt.
    – slhck
    May 17, 2013 at 13:15
  • 9
    You can make it even shorter, to prevent typos in the part that does not change: mv /folder1/folder2/folder3/file{,-2013}.txt
    – Jan Fabry
    May 18, 2013 at 21:19
74

Run the operation in a subshell.

( cd /folder1/folder2/folder3 && mv file.txt file-2013.txt )

The change of working directory won't be propagated to the parent shell.

7
  • 11
    +1: I like that one, more portable across shells than the brace-expansion trick (which is neat, but less portable) May 17, 2013 at 16:51
  • @Olivier, what makes you think brace expansion is not portable? Which shell do you have in mind that does not support it?
    – alexis
    May 17, 2013 at 17:54
  • 5
    @alexis Brace expansion is not specified by POSIX so is non portable "by design". ash, dash, ksh88 not to mention the old bourne shell are example of shells not supporting it.
    – jlliagre
    May 17, 2013 at 21:10
  • @jlliagre Which of the shells that you mentioned are fully POSIX conforming? That means they wouldn't have brace expansion even if it was POSIX. ksh88 was before POSIX was ratified; you should upgrade to at least ksh93. The only two anyone in Linux would care about are ash and dash since they are used in some small embedded distros (busybox, iirc?) and rescue disks.
    – Kaz
    May 20, 2013 at 3:21
  • 2
    @Kaz I care about portability of shell commands and the fact they are interactive or not doesn't matter. Of course, you are certainly free not to care about this but please accept that people think otherwise. The fact you always use bash or a shell that support barce-expansion doesn't means that's everyone's case.
    – jlliagre
    May 20, 2013 at 6:03
21

If you want clever, here's bash history expansion

mv /folder1/folder2/folder3/file.txt !#:1:h/file-2013.txt

I wouldn't use this myself since I find it impossible to memorize. I do occassionally use the vim equivalent, but have to look it up almost every time.

0
11

You can set a variable. Of course this has the side-effect of leaving the variables around.

D=/folder1/folder2/folder3; mv $D/file.txt $D/file-2013.txt
2
  • And, of course, you can avoid the side-effect of leaving the variable(s) around by putting the entire command line into a subshell: (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt), or simply by adding an unset command at the end. (I added quotes as a “best practice”; if you get in the habit of always using quotes, you won’t have to stop and scratch your head when a pathname that contains special characters comes along.) May 24, 2013 at 20:27
  • @Scott if you're going to use a subshell to eliminate side-effects, it's easier to do a cd than to set a variable. Not a lot easier, I admit. May 25, 2013 at 22:59
2

I like the other solutions, but here is another, implemented as a script with bash arrays, pushd, popd:

#!/bin/bash
set -e
# from http://stackoverflow.com/a/246128/178651
script_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# paths relative to the script
relative_paths=( \
path1 \
path2 \
path3 \
path4
)

for relative_path in "${relative_paths[@]}"
do
  pushd "$script_path/$relative_path" > /dev/null 2>&1
  pwd
  mv filename1 filename2
  # could do other stuff in this directory...
  popd > /dev/null 2>&1
done

pushd "$script_path" > /dev/null 2>&1
# could do other stuff in same directory as script...
popd > /dev/null 2>&1
1

Slhck directly answers the question in the simplest possible way, but Valter also likes the autopop answer, so here's one that's along the same lines;

pushd /folder1/folder2/folder3/; mv file.txt file-2013.txt; popd

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .