Shell Script to Copy and Prepend Folder Name to Files from Multiple Subdirectories

Shell script to copy and prepend folder name to files from multiple subdirectories

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.

Shell Script Copy All Files and Subdirectories - Folder Names With Spaces

Try:

cp -r "/shrdata/Legal/test location/test folder"/* "/shrdata/Legal/open access/"

For the * to work properly, it must be outside of quotes.

Example

Suppose that we have a test folder with two files:

$ ls -1 test\ folder/
file1
file2

Now, let's try putting the * in quotes:

$ echo "test folder/*"
test folder/*

Because the * is in quotes, it is not expanded into a list of filenames. Instead, it is merely treated as a literal character. Consequently, if we try to copy files this way, we will get a file-not-found error because no file is named *:

$ cp "test folder/*" target
cp: cannot stat ‘test folder/*’: No such file or directory

If we place the * outside of quotes, then pathname expansion will be performed:

$ echo "test folder"/*
test folder/file1 test folder/file2

This means that this form will work properly when used with cp.

Copy all files in folder to another folder prepending a . to the file name and rename it back to original file name

Let's suppose we have two directories as follows:

$ ls -a foo*
foo1:
. .. file1 file2 file3 'file 4'

foo2:
. ..

If we execute next command:

$ for file in foo1/* ; do base=$(basename "$file"); cp "foo1/$base" "foo2/.$base"; mv "foo2/.$base" "foo2/$base"; done

We will get at the end:

$ ls -a foo*
foo1:
. .. file1 file2 file3 'file 4'

foo2:
. .. file1 file2 file3 'file 4'

I think this is what you wanted.

Moreover, the file names with spaces inside will be correctly handled.

Copy multiple file from multiple directories with new filename

Note that above $file is set only by the for file in ... ; do ... ;done loop, i.e. in your xargs cmdline you were just using the last leftover value from the loop.

Some things to consider:

  • need to process each file separately => use xargs -l1 (process each 1 line).

  • need to separate DIR/FILENAME as the needed command is something like 'cp $DIR/$FILENAME $DIR/prefix-01-$FILENAME' (and prefix-02 also), use find ... -printf "%h %f\n" for this

  • for each line, need to do couple things (prefix-01,02) => use a scriptlet via sh -c '<scriptlet>'

  • better skip prefix-0?-*.jpg files from find, to be able to re-run it without "accumulating" copies

A possible implementation would be:

find . -type f \( -iname "*.jpg" ! -iname "special-*.jpg"  ! -name "prefix-0?-*.jpg" \) -printf "%h %f\n" | \
xargs -l1 sh -c 'cp -v "$1/$2" "$1/prefix-01-$2"; cp -v "$1/$2" "$1/prefix-02-$2"' --

As xargs runs sh -c '<scriptlet>' -- DIR FILE for each line, the scriptlet will properly evaluate $1 and $2 respectively.

--jjo

PS: directory separator in Unix-like systems is / :)

[Update: fixed to use %f instead of %P, as per comments below]

Copying files in multiple subdirectories in the Linux command line

Let the shell help you out:

find . -name '*.in' | while read old; do
new=${old%.in}.out # strips the .in and adds .out
cp "$old" "$new"
done

I just took the find command you said works and let bash read its output one filename at a time. So the bash while loop gets the filenames one at a time, does a little substitution, and a straight copy. Nice and easy (but not tested!).

Prefix every file in subdirectories with the folder name

You need to use Get-ChildItem (alias dir) and specify parameters Recurse and File at least, so your code will traverse subdirectories and rename files only (not also folder objects)

$rootFolder = 'D:\Test'   # the folder where the subfolders storing the PDF files are
(Get-ChildItem -Path $rootFolder -Filter '*.pdf' -File -Recurse) | Rename-Item -NewName {
'{0}_{1}' -f $_.Directory.Name, $_.Name
}

If you run the above several times, then each time the pdf files get their name prefixed with the directory name, so you'll end up with files like foo_foo_foo_file1.pdf.

To prevent that from happening, you can add a Where-Object clause like:

$rootFolder = 'D:\Test'
(Get-ChildItem -Path $rootFolder -Filter '*.pdf' -File -Recurse) |
Where-Object { $_.Name -notmatch "^$($_.Directory.Name)_" } |
Rename-Item -NewName { '{0}_{1}' -f $_.Directory.Name, $_.Name }

Note that the brackets around the Get-ChildItem cmdlet are needed to make sure it does not 'pick up' any file you have renamed earlier in the pipe



Related Topics



Leave a reply



Submit