How do I perform commands in another folder, without repeating the folder path?

73

32

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.

Valter Silva

Posted 2013-05-17T12:22:17.030

Reputation: 1 331

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 – 2013-05-17T16:37:31.683

2@user13107 they are on a different site so not duplicates – user151019 – 2013-05-18T07:13:53.000

1Mark, 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 – 2013-05-18T08:24:54.793

3@user13107, that's what you get for posting on the right site – Samuel Edwin Ward – 2013-05-18T14:08:23.560

Answers

125

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

Posted 2013-05-17T12:22:17.030

Reputation: 182 472

5I didn't know about the brace expansion, thank you! – Valter Silva – 2013-05-17T12:49:34.323

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 – 2013-05-17T12:54:23.567

5Are 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 – 2013-05-17T12:57:02.100

I run chsh and I'm using /bin/bash for sure, I don't know why this command don't work properly. – Valter Silva – 2013-05-17T13:09:04.720

22Note that there should be no space between file.txt, and file2.txt. – slhck – 2013-05-17T13:15:00.387

awesoooooome! I was putting space between the parameters, thank you sir! – Valter Silva – 2013-05-17T13:24:34.500

9You can make it even shorter, to prevent typos in the part that does not change: mv /folder1/folder2/folder3/file{,-2013}.txt – Jan Fabry – 2013-05-18T21:19:56.080

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

Posted 2013-05-17T12:22:17.030

Reputation: 100 516

11+1: I like that one, more portable across shells than the brace-expansion trick (which is neat, but less portable) – Olivier Dulac – 2013-05-17T16:51:02.740

@Olivier, what makes you think brace expansion is not portable? Which shell do you have in mind that does not support it? – alexis – 2013-05-17T17:54:12.937

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 – 2013-05-17T21:10:37.053

@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 – 2013-05-20T03:21:31.107

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 – 2013-05-20T03:22:26.423

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 – 2013-05-20T06:03:13.313

@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 – 2013-05-29T12:32:02.423

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

Posted 2013-05-17T12:22:17.030

Reputation: 18 546

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

Posted 2013-05-17T12:22:17.030

Reputation: 677

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 – 2013-05-24T20:27:04.793

@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 – 2013-05-25T22:59:34.757

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

Posted 2013-05-17T12:22:17.030

Reputation: 185

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

Posted 2013-05-17T12:22:17.030

Reputation: 2 645