7

I'm working in my terminal using bash trying to rename the folders:

1
2
3
4
5

I'd like to rename them according to a.txt file:

a 2
c 3
d 5
e 1
b 4

The order in the .txt file is completely random. I'm hoping to find a way where I can rename the folder so they become:

1 > e
2 > a
3 > c
4 > b
5 > d

Is there any way to do this?

Thanks a lot!

  • 1
    The question assumes there are no more then 26 fles to rename. Is that correct? – Jacob Vlijm May 31 '16 at 8:13
  • I've got 1 file and 200 folders to rename :( – Ditte May 31 '16 at 8:16
  • Ah, that makes the question more interesting. Any chance on name clashes? – Jacob Vlijm May 31 '16 at 8:16
  • 1
    @BharadwajRaju I have already voted to do so. – muru May 31 '16 at 8:51
  • 1
    @Ditte You use OS X and iOS. Why ask here, where it is off-topic? Ask at SuperUser or AskDifferent. – UniversallyUniqueID May 31 '16 at 8:58
4

Assuming the names in the list do not include spaces, other than between the names, the script below will rename folders from a textfile recursively. It will read the lines of the file, split them into two (new name, old name), rename occuring folders which are named by the second string in the line, into the first string in the line.

It will do this, as mentioned recursively, from bottom to top in the directory tree. This prevents issues in the script, attempting to rename folders in possibly already renamed parent folders.

#!/usr/bin/env python
import os
import shutil

# -- set the (absolute) path to the targeted folder below
dr = "/home/jacob/Bureaublad/testfolder"
# -- set the path to the textfiles below
ren = "/home/jacob/Bureaublad/testfolder/renfile.txt"

renlist = [cpl for cpl in [l.split() for l in open(ren).readlines()] if cpl]
currnames = [r[1] for r in renlist]

for root, dirs, files in os.walk(dr, topdown = False):
    for dr in dirs:
        try:
            # look up the name, pass if not in the list
            i = currnames.index(dr)
        except ValueError:
            pass
        else:
            newname = renlist[i][0]
            shutil.move(root+"/"+dr, root+"/"+newname)

How to use

  1. Copy the script into an empty file (in a plain text editor), save it as rename.py
  2. In the head section of the script, set the real paths to the textfile and the directory
  3. run it by the command (in a terminal):

    python /path/to/rename.py
    

Explanation

The script:

  • creates a list of name- couples: newname/oldname
  • using python's os.walk(), it looks through your directories recursively, from bottom to top.
  • If the folder's name matches the second item of a name couple, it renames the folder into the first name.
  • If the folder's name is not listed, it will be untouched.
  • is the script complete? – Ajay Kulkarni May 31 '16 at 8:58
  • Yep, but still writing an explanation :) – Jacob Vlijm May 31 '16 at 8:59
  • @jacobvlijm Thanks! Do I just change the paths to my folders and text and then copy paste it in my terminal. Have never used python before – Ditte May 31 '16 at 8:59
  • @Ditte nono, see description... – Jacob Vlijm May 31 '16 at 9:01
  • 1
    @Ditte In what application did you paste it? it should be in a plain text application. see stackoverflow.com/a/21639459/1391444 – Jacob Vlijm May 31 '16 at 10:29
6

You could use xargs with mv:

xargs -r -a /path/to/file.txt -L1 mv

(assuming you're in the directory where the files are located).

xargs converts input to arguments for a command.

  • -a foo to read input from foo.
  • -L1 to use exactly one line of input per command invocation.
  • -r to avoid running the command if the line is empty

For example, using echo mv instead of mv to show the commands being run:

$ xargs -r -a foo.txt -L1 echo mv
mv a 2
mv c 3
mv d 5
mv e 1
mv b 4

You can also use the -p option to make xargs confirm before running the command:

$ xargs -r -p -a foo.txt -L1 mv
mv a 2 ?...n
mv c 3 ?...n
mv d 5 ?...y
mv e 1 ?...mv: cannot stat ‘d’: No such file or directory
n
mv b 4 ?...
  • will not work by definition in a directory recursively, needs to be from bottom to top. – Jacob Vlijm May 31 '16 at 8:30
  • It says:xargs: illegal option -- r usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr] [-L number] [-n number [-x]] [-P maxprocs] [-s size] [utility [argument ...]] – Ditte May 31 '16 at 8:36
  • @Ditte it's a legal option from at least 12.04: manpages.ubuntu.com/manpages/precise/en/man1/xargs.1.html What version of Ubuntu are you using? – muru May 31 '16 at 8:37
  • I'm on iOS. Is that a problem? I've used quite a lot of answers from in here, when I've been extracting info from large datasets and it usually works nicely. Hope that I haven't bothered you unnecessarily :( – Ditte May 31 '16 at 8:39
  • 2
    Yes, that is a problem. Questions on non-Ubuntu systems should be asked on Unix & Linux. As can be seen here, commands can be different. – muru May 31 '16 at 8:40
2

There is one entry per line in the a.txt file, so the names listed do not contain \n (new line) char, right?

A line contains 2 names separated with a space char, so the names do not contain space char, right? (or another possibility would be to use fixed length for the names listed).

Give a try to this, if the names do not contain neither \n nor space chars:

awk '{printf("%s\0%s\0",$2,$1)}' a.txt | xargs -0 -L 2 mv

awk is used to reverse the order of names in lines read from a.txt file. \0 is used to separate the names when they are printed to standard output.

xargs reads the standard input:

  • -0: \0 is used as separator for the elements read from standard input
  • -L 2 mv: mv command is invoked with 2 parameters at a time.

The test:

$ ls -al
total 40
drwxrwxr-x  7 stacko stacko  4096 May 31 11:25 .
drwxrwxr-x 21 stacko stacko 12288 May 31 11:23 ..
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 1
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 2
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 3
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 4
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 5
-rw-rw-r--  1 stacko stacko    20 May 31 11:24 a.txt
$ cat a.txt
a 2
c 3
d 5
e 1
b 4
$ awk '{printf("%s\0%s\0",$2,$1)}' a.txt | xargs -0 -L 2 mv -v
'2' -> 'a'
'3' -> 'c'
'5' -> 'd'
'1' -> 'e'
'4' -> 'b'
$ ls -al
total 40
drwxrwxr-x  7 stacko stacko  4096 May 31 11:26 .
drwxrwxr-x 21 stacko stacko 12288 May 31 11:23 ..
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 a
-rw-rw-r--  1 stacko stacko    20 May 31 11:24 a.txt
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 b
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 c
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 d
drwxrwxr-x  2 stacko stacko  4096 May 31 11:25 e
2

In the simple case you show above, where each line has two "words", the target directory and the new name, and where neither can contain any whitespace, you can simply do:

while read -r from to; do mv "$from" "$to"; done < file 

That will read each line in file assign the first string (until the first whitespace) to $from and the rest of the line to $to and then run mv on each of them.

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.