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.

Peter Mortensen
  • 12,090
  • 23
  • 70
  • 90
Valter Silva
  • 1,371
  • 7
  • 23
  • 32
  • 9
    I can't believe this has so many upvotes. It's a duplicate http://unix.stackexchange.com/questions/35782/quick-way-to-include-a-directory-path-when-calling-mv and http://unix.stackexchange.com/questions/66889/minimal-command-to-make-a-copy-of-a-file – user13107 May 17 '13 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 '13 at 16:37
  • 2
    @user13107 they are on a different site so not duplicates – mmmmmm May 18 '13 at 07: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 '13 at 08:24
  • 3
    @user13107, that's what you get for posting on the right site – Samuel Edwin Ward May 18 '13 at 14:08

6 Answers6

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.

slhck
  • 223,558
  • 70
  • 607
  • 592
  • 5
    I didn't know about the `brace` expansion, thank you! – Valter Silva May 17 '13 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` – Valter Silva May 17 '13 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 '13 at 12:57
  • I run `chsh` and I'm using `/bin/bash` for sure, I don't know why this command don't work properly. – Valter Silva May 17 '13 at 13:09
  • 22
    Note that there should be no space between `file.txt,` and `file2.txt`. – slhck May 17 '13 at 13:15
  • awesoooooome! I was putting space between the parameters, thank you sir! – Valter Silva May 17 '13 at 13:24
  • 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 '13 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.

Ignacio Vazquez-Abrams
  • 111,361
  • 10
  • 201
  • 247
  • 11
    +1: I like that one, more portable across shells than the brace-expansion trick (which is neat, but less portable) – Olivier Dulac May 17 '13 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 '13 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 '13 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 '13 at 03:21
  • Who cares about portability of interactive commands; that's more of a scripting concern. Why avoid doing easy things in bash today just because they cannot be done in ash which you may have to use tomorrow. Today is today, and what you're typing just has to be portable to the program that is accepting the input at the moment. – Kaz May 20 '13 at 03:22
  • 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 '13 at 06:03
  • @Kaz: today I regularly (every day) work on production system with software so old that : tar doesn't take out the beginning "/" when extracting files, bash is so old it doesn't support arrays and "BASH_SOURCE" and the like, awk is probably close to the original one, etc. No fancy gnu stuff (and impossible to have them installed without asking for a multi-man-month overhaul of the whole array of (numerous) servers and vms. So yes, I do care about portability (and am painfully aware when someone present a solution that is not portable). (fwiw, brace expansion do work on that bash, but still...) – Olivier Dulac May 29 '13 at 12:32
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.

glenn jackman
  • 25,463
  • 6
  • 46
  • 69
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
sjbotha
  • 971
  • 6
  • 6
  • 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.) – Scott - Слава Україні May 24 '13 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. – Isaac Rabinovitch May 25 '13 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
Gary S. Weaver
  • 205
  • 2
  • 9
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
Isaac Rabinovitch
  • 2,804
  • 1
  • 23
  • 30