Shell script to copy and prepend folder name to fi

2019-02-05 05:27发布

问题:

I have several folders with different images sharing file names, with a folder structure like this:

/parent/folder001/img001.jpg
/parent/folder001/img002.jpg
/parent/folder002/img001.jpg
/parent/folder002/img002.jpg
/parent/folder003/img001.jpg
/parent/folder003/img002.jpg
...

and would like to copy/rename these files into a new folder, like this:

/newfolder/folder001_img001.jpg
/newfolder/folder001_img002.jpg
/newfolder/folder002_img001.jpg
/newfolder/folder002_img002.jpg
/newfolder/folder003_img001.jpg
/newfolder/folder003_img002.jpg
...

(It's probably better if newfolder isn't a subfolder of parent, since that might end up causing really weird recursion.)

None of the folders containing images have any subfolders.

Ideally, I'd like to be able to reuse the script to "update" newfolder, since I might need to add more folders-containing-images later along the line.

How can I accomplish this with a shell script?

回答1:

This is a bit tedious but will do:

#!/bin/bash
parent=/parent
newfolder=/newfolder
mkdir "$newfolder"
for folder in "$parent"/*; do
  if [[ -d "$folder" ]]; then
    foldername="${folder##*/}"
    for file in "$parent"/"$foldername"/*; do
      filename="${file##*/}"
      newfilename="$foldername"_"$filename"
      cp "$file" "$newfolder"/"$newfilename"
    done
  fi
done

Put the parent path to parent variable and newfolder path to newfolder variable.



回答2:

functions to the rescue, updating Jahid's script:

function updateFolder
{
    mkdir "$2"
    for folder in "$1"/*; do
    if [[ -d $folder ]]; then
        foldername="${folder##*/}"
        for file in "$1"/"$foldername"/*; do
            filename="${file##*/}"
            newfilename="$foldername"_"$filename"
            cp "$file" "$2"/"$newfilename"
        done
    fi
    done
}

used:

$ updateFolder /parent /newfolder


回答3:

I would use something like this:

find -type f -exec sh -c 'f={}; fnew=$(rev <<< "$f" | sed 's~/~_~' | rev); echo "mv $f $fnew"' \;

This looks for files within the current directory structure and perform the following:

  • get the file name
  • replace the last / with _.
  • write echo "cp $old_file $new_file"

Once you have run this and seen it prints the proper command, remove the echo so that it effectively does the mv command.

I know the fnew=$(rev <<< "$f" | sed 's~/~_~' | rev) is a bit ugly trick, but I couldn't find a better way to replace the last / with _. Maybe sed -r 's~/([^/]*)$~_\1~' could also be appropiate, but I always like using rev :)


Since your find does not behave well with the -sh c expression, let's use a while loop for it:

while read -r file
do
    new_file=$(rev <<< "$file" | sed 's~/~_~' | rev)
    echo "mv $file $new_file"; done < <(find . -type f)
done < <(find . -type f)

As a one-liner:

while read -r file; do new_file=$(rev <<< "$file" | sed 's~/~_~' | rev); echo "mv $file $new_file"; done < <(find . -type f)