Copying files from multiple directories into a sin

2020-04-21 07:12发布

问题:

There are multiple directories which contain a file with the same name:

direct_afaap/file.txt
direct_fgrdw/file.txt
direct_sardf/file.txt
...

Now I want to extract them to another directory, direct_new and with a different file name such as:

[mylinux~ ]$ ls direct_new/
file_1.txt  file_2.txt  file_3.txt

How can I do this?

BTW, if I want to put part of the name in original directory into the file name such as:

[mylinux~ ]$ ls direct_new/
file_afaap.txt  file_fgrdw.txt  file_sardf.txt

What can I do?

回答1:

This little BaSH script will do it both ways:

#!/bin/sh
#

# counter
i=0

# put your new directory here
# can't be similar to dir_*, otherwise bash will
# expand it too
mkdir newdir

for file in `ls dir_*/*`; do
    # gets only the name of the file, without directory
    fname=`basename $file`
    # gets just the file name, without extension
    name=${fname%.*}
    # gets just the extention
    ext=${fname#*.}

    # get the directory name
    dir=`dirname $file`
    # get the directory suffix
    suffix=${dir#*_}

    # rename the file using counter
    fname_counter="${name}_$((i=$i+1)).$ext"

    # rename the file using dir suffic
    fname_suffix="${name}_$suffix.$ext"

    # copy files using both methods, you pick yours
    cp $file "newdir/$fname_counter"
    cp $file "newdir/$fname_suffix"
done

And the output:

$ ls -R
cp.sh*
dir_asdf/
dir_ljklj/
dir_qwvas/
newdir/
out

./dir_asdf:
file.txt

./dir_ljklj:
file.txt

./dir_qwvas:
file.txt

./newdir:
file_1.txt
file_2.txt
file_3.txt
file_asdf.txt
file_ljklj.txt
file_qwvas.txt


回答2:

while read -r line; do 
   suffix=$(sed 's/^.*_\(.*\)\/.*$/\1/' <<<$line)
   newfile=$(sed 's/\.txt/$suffix\.txt/' <<<$line)
   cp "$line" "~/direct_new/$newfile"
done <file_list.txt

where file_list is a list of your files.



回答3:

You can achieve this with Bash parameter expansion:

dest_dir=direct_new

# dir based naming
for file in direct_*/file.txt; do
  [[ -f "$file" ]] || continue # skip if not a regular file
  dir="${file%/*}"             # get the dir name from path
  cp "$file" "$dest_dir/file_${dir#*direct_}.txt"
done

# count based naming
counter=0
for file in direct_*/file.txt; do
  [[ -f "$file" ]] || continue # skip if not a regular file
  cp "$file" "$dest_dir/file_$((++counter)).txt"
done
  • dir="${file%/*}" removes all characters starting from /, basically, giving us the dirname
  • ${dir#*direct_} removes the direct_ prefix from dirname
  • ((++counter)) uses Bash arithmetic expression to pre-increment the counter

See also:

  • Why you shouldn't parse the output of ls(1)
  • Get file directory path from file path
  • How to use double or single brackets, parentheses, curly braces


回答4:

It may not be quite what you want, but it will do the job. Use cp --backup=numbered <source_file> <destination_directory:

$ find . -name test.sh
./ansible/test/integration/roles/test_command_shell/files/test.sh
./ansible/test/integration/roles/test_script/files/test.sh
./Documents/CGI/Code/ec-scripts/work/bin/test.sh
./Documents/CGI/Code/ec-scripts/trunk/bin/test.sh
./Test/test.sh
./bin/test.sh
./test.sh

$ mkdir BACKUPS

$ find . -name test.sh -exec cp --backup=numbered {} BACKUPS \;
cp: './BACKUPS/test.sh' and 'BACKUPS/test.sh' are the same file

$ ls -l BACKUPS
total 28
-rwxrwxr-x. 1 jack jack 121 Jun  9 10:29 test.sh
-rwxrwxr-x. 1 jack jack  34 Jun  9 10:29 test.sh.~1~
-rwxrwxr-x. 1 jack jack  34 Jun  9 10:29 test.sh.~2~
-rwxrwxr-x. 1 jack jack 388 Jun  9 10:29 test.sh.~3~
-rwxrwxr-x. 1 jack jack 388 Jun  9 10:29 test.sh.~4~
-rwxrwxr-x. 1 jack jack  20 Jun  9 10:29 test.sh.~5~
-rwxrwxr-x. 1 jack jack 157 Jun  9 10:29 test.sh.~6~

If you really want to put part of the folder name in, you have to decide exactly what part you want. You could, of course, just replace the directory separator character with some other character, and put the whole path into the filename.



标签: linux bash shell