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!

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

4 Answers 4

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 ?...
8
  • will not work by definition in a directory recursively, needs to be from bottom to top. May 31, 2016 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, 2016 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, 2016 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, 2016 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, 2016 at 8:40
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.
16
  • is the script complete? May 31, 2016 at 8:58
  • Yep, but still writing an explanation :) May 31, 2016 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, 2016 at 8:59
  • @Ditte nono, see description... May 31, 2016 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 May 31, 2016 at 10:29
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.

0

You must log in to answer this question.

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