renaming folders using a .txt file

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!


Solution 1:

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 ?...

Solution 2:

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.

Solution 3:

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

Solution 4:

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.