简体   繁体   中英

Bash: how to copy multiple files with same name to multiple folders

I am working on Linux machine. I have a lot of files named the same, with a directory structure like this:

P45_input_foo/result.dat
P45_input_bar/result.dat
P45_input_tar/result.dat
P45_input_cool/result.dat ...

It is difficult to copy them one by one. I want to copy them into another folder named as data with similar folder names and file names:

/data/foo/result.dat
/data/bar/result.dat
/data/tar/result.dat
/data/cool/result.dat ...

In stead of copy them one by one what I should do?

You will need to extract the 3rd item after "_" :

P45_input_foo --> foo

create the directory (if needed) and copy the file to it. Something like this (not tested, might need editing):

 STARTING_DIR="/"
cd "$STARTING_DIR"
VAR=$(ls -1)
while read DIR; do
  TARGET_DIR=$(echo "$DIR" | cut -d'_' -f3)
  NEW_DIR="/data/$DIR"
  if [ ! -d "$NEW_DIR" ]; then
    mkdir "$NEW_DIR"
  fi
  cp "$DIR/result.dat" "$NEW_DIR/result.dat"
  if [ $? -ne 0 ];
    echo "ERROR: encountered an error while copying"
  fi
done <<<"$VAR"

Explanation: assuming all the paths you've mentioned are under root / (if not change STARTING_PATH accordingly). With ls you get the list of the directories, store the output in VAR . Pass the content of VAR to the while loop.

Using a for loop in bash :

# we list every files following the pattern : ./<somedirname>/<any file>
# if you want to specify a format for the folders, you could change it here
# i.e. for your case you could write 'for f in P45*/*' to only match folders starting by P45 
for f in */*
do
    # we strip the path of the file from its filename
    # i.e. 'P45_input_foo/result.dat' will become 'P45_input_foo'
    newpath="${f%/*}"

    # mkdir -p /data/${newpath##*_} will create our new data structure
    # - /data/${newpath##*_} extract the last chain of character after a _, in our example, 'foo'
    # - mkdir -p will recursively create our structure
    # - cp "$f" "$_" will copy the file to our new directory. It will not launch if mkdir returns an error
    mkdir -p /data/${newpath##*_} && cp "$f" "$_"
done

the ${newpath##*_} and ${f%/*} usage are part of Bash string manipulation methods. You can read more about it here .

A bit of find and with a few bash tricks, the below script could do the trick for you. Remember to run the script without the mv and see if "/data/"$folder"/" is the actual path that you want to move the file(s).

#!/bin/bash

while IFS= read -r -d '' file
do

    fileNew="${file%/*}"       # Everything before the last '\'
    fileNew="${fileNew#*/}"    # Everything after the last '\'

    IFS="_" read _ _ folder <<<"$fileNew"

    mv -v "$file"  "/data/"$folder"/"

done < <(find . -type f -name "result.dat" -print0)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM