Copy same files at multiple locations in UNIX - unix

I have below 3 Files at source Location ${PDR}
PHONE_ABC.txt
PHONE_EFG.txt
PHONE_XYZ.txt
I need to copy same files at 3 different locations and add time and some text to it:
Target Location 1 ${LCT1}
Target Location 2 ${LCT2}
Target Location 3 ${LCT3}
Files at Location 1 should be as below:
PHONE_ABC_LCT1_20180914.txt
PHONE_EFG_LCT1_20180914.txt
PHONE_XYZ_LCT1_20180914.txt
Files at Location 2 should be as below:
PHONE_ABC_LCT2_20180914.txt
PHONE_EFG_LCT2_20180914.txt
PHONE_XYZ_LCT2_20180914.txt
Files at Location 3 should be as below:
PHONE_ABC_LCT3_20180914.txt
PHONE_EFG_LCT3_20180914.txt
PHONE_XYZ_LCT3_20180914.txt
Code Used
#!/usr/bin/ksh
cp ${PDR}/PHONE_*.txt ${LCT1}/
cp ${PDR}/PHONE_*.txt ${LCT2}/
cp ${PDR}/PHONE_*.txt ${LCT3}/
# define list of files
LCT1=${LCT1}/PHONE_*.txt
LCT2=${LCT2}/PHONE_*.txt
LCT3=${LCT3}/PHONE_*.txt
# grab time
dtstamp=`date +%Y%m%d`
# for LCT1
for file in ${LCT1}
do
if [ ! -s ${file} ]
then
continue
fi
filebase=${file%.csv}
mv ${file} ${filebase}_LCT1_${dtstamp}.txt
done
# for LCT2
for file in ${LCT2}
do
if [ ! -s ${file} ]
then
continue
fi
filebase=${file%.csv}
mv ${file} ${filebase}_LCT2_${dtstamp}.txt
done
# for LCT3
for file in ${LCT3}
do
if [ ! -s ${file} ]
then
continue
fi
filebase=${file%.csv}
mv ${file} ${filebase}_LCT3_${dtstamp}.txt
done
This is giving me what i require. But somehow i believe this code could be made more efficient and robust which i am not able to figure it out.
Also on day 2 it keeps appending timestamp to the files at target location which i don't thing is good thing to do.
Any pointers to make this code look more efficient and good.

Something like that maybe :
#!/usr/bin/ksh
# grab time
dtstamp=$(date +"%Y%m%d")
cd ${PDR}
for file in PHONE_*.txt
do
if [ ! -s ${file} ]
then
continue
fi
cp ${file} ${LCT1}/${file%.txt}_LCT1_${dtstamp}.csv
cp ${file} ${LCT2}/${file%.txt}_LCT2_${dtstamp}.csv
cp ${file} ${LCT3}/${file%.txt}_LCT3_${dtstamp}.csv
done

Related

Rename files based on pattern in path

I have thousands of files named "DOCUMENT.PDF" and I want to rename them based on a numeric identifier in the path. Unfortunately, I don't seem to have access to the rename command.
Three examples:
/000/000/002/605/950/ÐÐ-02605950-00001/DOCUMENT.PDF
/000/000/002/591/945/ÐÐ-02591945-00002/DOCUMENT.PDF
/000/000/002/573/780/ÐÐ-02573780-00002/DOCUMENT.PDF
To be renamed as, without changing their parent directory:
2605950.pdf
2591945.pdf
2573780.pdf
Use a for loop, and then use the mv command
for file in *
do
num=$(awk -F "/" '{print $(NF-1)}' file.txt | cut -d "-" -f2);
mv "$file" "$num.pdf"
done
You could do this with globstar in Bash 4.0+:
cd _your_base_dir_
shopt -s globstar
for file in **/DOCUMENT.PDF; do # loop picks only DOCUMENT.PDF files
# here, we assume that the serial number is extracted from the 7th component in the directory path - change it according to your need
# and we don't strip out the leading zero in the serial number
new_name=$(dirname "$file")/$(cut -f7 -d/ <<< "$file" | cut -f2 -d-).pdf
echo "Renaming $file to $new_name"
# mv "$file" "$new_name" # uncomment after verifying
done
See this related post that talks about a similar problem: How to recursively traverse a directory tree and find only files?

unix: Can I delete files in a directory that do not contain text?

Can I delete files in a directory that do NOT contain any text? These are text files with the extension '.fasta'. Initially I am running this script:
for g in `cat genenames.txt` ; do cat *${g}_*.fa > $g.fasta ; done
On a list of files that look like:
id_genename_othername.fa
But in some directories, not all the genenames from the list (genenames.txt) have files with names that match. So sometimes I will get this message:
cat: *genename_*.fa: No such file or directory
The above code still makes a '.fasta' file with the genename that doesn't exist and I would like to remove it. THANK YOU.
Assuming your script is using #!/bin/bash, I'd do
shopt -s nullglob
while IFS= read -r pattern; do
files=( *"$pattern"*.fa )
if [[ "${#files[#]}" -eq 0 ]]; then
echo "no files match pattern *$pattern*.fa"
else
cat "${files[#]}" > $pattern.fasta
fi
done < genenames.txt
Have you tried the following?
for g in `cat genenames.txt` ; do cat *${g}_*.fa 2>/dev/null > $g.fasta ; done
This should prevent the not found errors from producing files

Rename files in a directory the simplest way in a script

I want to write a script that add '0' at the end of the files that doesn't have it.
This is what I wrote:
#!/bin/bash
for file in $1
do
echo $file
ls $file | grep "\0$"
if ["$?"="1"]
then
fi
done
I don't know hot to target the files in a way I can rename them
for file in *[!0]; do mv "$file" "${file}0"; done
For each name that does not end 0, rename it so it does. Note that this handles names with spaces etc in them.
I want to give the script a directory, and it will rename the files in it that do not end in 0. How can I use this in a way I can tell the script which directory to work with?
So, make the trivial necessary changes, working with a single directory (and not rejecting the command line if more than one directory is specified; just quietly ignoring the extras):
for file in "${1:?}"/*[!0]; do mv "$file" "${file}0"; done
The "${1:?}" notation ensures that $1 is set and is not empty, generating an error message if it isn't. You could alternatively write "${1:-.}" instead; that would work on the current directory instead of a remote directory. The glob then generates the list of file names in that directory that do not end with a 0 and renames them so that they do. If you have Bash, you can use shopt -s nullglob you won't run into problems if there are no files without the 0 suffix in the directory.
You can generalize to handle any number of arguments (all supposed to be directories, defaulting to the current directory if no directory is specified):
for dir in "${#:-.}"
do
for file in "$dir"/*[!0]; do mv "$file" "${file}0"; done
done
Or (forcing directories):
for dir in "${#:-.}"
do
(cd "$dir" && for file in *[!0]; do mv "$file" "${file}0"; done)
done
This has the merit of reporting which arguments are not directories, or are inaccessible directories.
There are endless variations of this sort that could be made; some of them might even be useful.
Now, I want to do the same but, instead of the file ending with '0', the script should rename files that do not end with '.0' so that they do end with '.0'?
This is slightly trickier because of the revised ending. Simply using [!.][!0] is insufficient. For example, if the list of files includes 30, x.0, x0, z.9, and z1, then echo *[!.][!0] only lists z1 (omitting 30, x0 and z.9 which do not end with .0).
I'd probably use something like this instead:
for dir in "${#:-.}"
do
(
cd "$dir" &&
for file in *
do
case "$file" in
(*.0) : skip it;;
(*) mv "$file" "${file}0";;
esac
done
)
done
The other alternative lists more glob patterns:
for dir in "${#:-.}"
do
(cd "$dir" && for file in *[!.][!0] *.[!0] *[!.]0; do mv "$file" "${file}0"; done)
done
Note that this rapidly gets a lot trickier if you want to look for files not ending .00 — there would be a 7 glob expressions (but the case variant would work equally straight-forwardly), and shopt -s nullglob becomes increasingly important (or you need [ -f "$file" ] && mv "$file" "${file}.0" instead of the simpler move command).

Files overwritten during cp loop

I'm trying to copy 4 files that are present in Folder A (main folder) into all subfolders (1000) present in Folder A. To do this I used:
for i in *;
do
cp File1.bash $i
cp File2.sh $i
cp File3.R $i
cp File4.R $i
done
Unfortunately, the content of all the files after the cp is overwritten in destination directories. In other words files: File2.sh, File3.R and File4.R have the content of File1.bash.
Can anyone help me to solve this problem?
Thank you very much
The problem here is that the wildcard * is replaced by every file in the directory, including File2.sh,, File3.R and so on. So, at one point during the loop, $i will be 'File2.sh,' and you will execute the command cp File1.bash File2.sh, which will overwrite File2.sh (the same problem happens for the other files).
You should replace the wildcard with a command which only list directory such as ls -d */.
For instance :
for i in $(ls -d */)
do
cp File1.bash $i
cp File2.sh $i
cp File3.R $i
cp File4.R $i
done
Also , note that cp can take multiple arguments as source, so cp File1.bash File2.sh File3.R File4.R $i should do what you want. It is also less error prone as the last parameter is expected to be a directory, so cp will give an error if $i is a regular file.
The for cycle goes through all files and directories (except ones which name starts with a dot) in the current directory including the files File1.bash, File2.s, File3.R, File4.R so sooner or later they will appear in the destination of the cp commands and get overwritten.
There are multiple ways how to resolve the problem:
Expand just directories
for i in */ ; do
cp File1.bash File2.sh File3.R File4.R "$i"
done
Test if the destination is a directory
for i in * ; do
if test -d "$i" ; then
cp File1.bash File2.sh File3.R File4.R "$i"
fi
done
Compared to the first answer this code does not need to call an additional external command (ls) and it is not a good idea to parse output of ls anyway :) (It could contain some unexpected content.)
Move the source files to a different place
For example move the files to directory called .template (the dot is important) or to a directory outside the current directory (../template):
mv File1.bash File2.sh File3.R File4.R .template
Changed script (will not cycle through the hidden .template):
source=.template
for i in * ; do
cp "$source/File1.bash" "$i"
cp "$source/File2.sh" "$i"
cp "$source/File3.R" "$i"
cp "$source/File4.R" "$i"
done
Using double quotes
It is a good idea to enclose string where you expand variables between double quotes. Then the script will correctly work with string containing spaces newlines etc. too.

Recursive logic in shell scripts

Here is a problem that i need solution for:
Say file A contains names of files B,C,D. And file B contains file names E,F,G etc. File C contains names of files H,I,J and so on......
I have to parse the files starting from A ,and copy the files mentioned in A to dir DIR. I wanna do the same parsing on all the child files B,C,D and get their child files into my dir DIR. This should go on until i reach the last file say Z which doesn't contain any other file names.
How do i do that?
I wanna do the whole thing in a single script and any further optimization would be appreciated.
Thanks in advance.
If the files contain other data than file names more parsing could be necessary.
DIR="$HOME/DIR"
startfile="a"
counter=0
copy_to_dir ()
{
while read line ; do
if [ -f "$line" ] ; then
cp "$line" "$2" && ((counter++))
copy_to_dir "$line" "$2" # recurse
fi
done < "$1"
} # ---------- end of function copy_to_dir ----------
if [ -f "$startfile" -a -d "$DIR" ] ; then
copy_to_dir "$startfile" "$DIR" # start copying
fi
printf "'%s' : %d files copied to '%s'\n" "$0" $counter "$DIR"

Resources