0

I am trying to rename a few directories that contain "Fever" to contain "Malaria" instead. The instruction is to do it without sed or rename. So far, my errors include mostly lines like mv: cannot stat ‘retest\nretest/Section-01\nretest/Section-02\nretest/Section-03\nretest/Section-04’: No such file or directory.

The best my code have done is rename directories in the first level.

Here's my directory structure:

Fever-A/Malaria-A-A

Fever-B/Fever-B-A

Fever-B/Fever-B-B

Fever-C/Malaria-A

Fever-C/Fever-C-A

Fever-C/Fever-C-B

Fever-C/Fever-C-C-C

Fever-D/Malaria-A

Fever-D/Malaria-B

The code I have so far is :

#!/bin/bash

# Access directory
#cd $1

# Find all subdirectories in $1 and load up array
all=($(find $1 -type d))
#echo ${all[@]}

# Loop through directories above
for dir in ${all[@]}
do
    # echo "$dir"
    cd $dir
    # List files with "Section" in name
    subdir=(:"Section*")

    # A second loop for directories in each dir with "Section*"
    for item in ${subdir[@]}
    do
            echo $item
            echo "--------------------"

            # Rename operation
            mv $item ${item//Fever/Malaria}
    done
    cd $1
done

Another approach I've considered is using a function like so, but it's not working either:

#!/bin/bash

rename(){
    old_names=($(find $1 -maxdepth 1 -type d))

    for item in ${old_names[@]}
    do
            if [[ $item = *Section* ]]; then
                    new_name=${item//Fever/Malaria}
                    mv $item $new_name
            elif [[ $1 != $item ]]; then
                    rename $item
            fi

            rename $1
    done
}

rename $1
  • 1
    There are several questions on this subject if you search rename tag... There are answers that show how to do this properly (that is, using find with -exec and -depth) and some of them use just parameter expansion (no sed, no perl rename). – don_crissti Jul 28 '16 at 22:26
  • What should the resulting directory structure look like? Please edit the question with these results. – Stephen Harris Jul 28 '16 at 22:28
  • "The instruction is" -- is this homework? :) Or is there some other reason to specifically not use sed? – ilkkachu Jul 29 '16 at 6:48
  • @ilkkachu Tools like sed and rename make things easy true, but I'm wondering what really happens under the hood. The resulting directory structure remains the same. – IshMary Jul 30 '16 at 7:04
  • Under the hood, a bunch of getdents(2) (or readdir(3)) calls are made to list the files during a recursive walk of the tree, and the files are then moved with calls to rename(2)... You could implement the recursive logic with shell functions, but to get a bit closer to the OS interface, you'd better write a C program. – ilkkachu Jul 30 '16 at 7:57
1

As Don Crissti mentions, there are a lot of ways of solving this with find. But I thought, since we're only talking two directory levels, that we can do this slightly differently with only one external command: mv

#!/bin/bash

fixdir()
{
  local f g

  for f in Fever*
  do
    if [ -e "$f" ]
    then
      g=Malaria"${f#Fever}"
      if [ -e "$g" ]
      then
        echo "Skipping $1/$f->$1/$g; already exists"
      else
        # echo "Renaming $1/$f->$1/$g"
        mv "$f" "$g"
      fi
    fi
  done
}

for a in *
do
  if [ -d "$a" ]
  then
    (cd $a ; fixdir $a )
  fi
done

fixdir .

The results:

$ ls -1d F*/*
Fever-A/Malaria-A-A
Fever-B/Fever-B-A
Fever-B/Fever-B-B
Fever-C/Fever-C-A
Fever-C/Fever-C-B
Fever-C/Fever-C-C-C
Fever-C/Malaria-A
Fever-D/Malaria-A
Fever-D/Malaria-B

$ ./fix

$ ls -1d M*/*
Malaria-A/Malaria-A-A
Malaria-B/Malaria-B-A
Malaria-B/Malaria-B-B
Malaria-C/Malaria-A
Malaria-C/Malaria-C-A
Malaria-C/Malaria-C-B
Malaria-C/Malaria-C-C-C
Malaria-D/Malaria-A
Malaria-D/Malaria-B
0

What about:

for dir in $(find /startdir -depth -type d -name '*Fever*')
do   dn=$(basename $dir)
     mv $dir $(dirname $dir)/${dn//Fever/Malaria}
done

That should do it. (Change startdir to your start directory)

The find will only return directories containing 'Fever'. the mv command uses bash inline substitution to return a string with Fever changed to Malaria.

The added -depth makes it process subdirs first.

  • What happens to /startdir/Fever-A/Fever-A ? That'll result in mv /startdir/Fever-A/Fever-A /startdir/Malaria-A/Malaria-A but that directory doesn't exist... or if 'Fever-A' gets renamed first then the find will then start looking for a directory that doesn't exist and won't find the new Malaria-A/Fever-A. – Stephen Harris Jul 28 '16 at 22:41
  • Good catch! Added a -depth to process subdirs first, and some more cruft to rename only the end directory segment. – P. Heffner Jul 29 '16 at 1:01
  • 1
    Good update. Now the next problem; what if there's a directory called Fever A-B - ie has a space in it? (I'm not picking on you; this is a good learning opportunity - there's a lot of hidden gotchas in something so simple! Let me know if you don't want to do this here and I'll stop) – Stephen Harris Jul 29 '16 at 1:19

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.