Having trouble implementing cp -u in shell script - linux

For a school project, I have a shell script that is supposed to copy the files in two directories (without looking at subdirectories) into a third directory. I'm testing out the -u command so that if two files have the same name, only the newer one will get copied over (that's also a spec). My shell script looks like this (excluding #! and error checking):
cd $1 #first directory
for file in `ls`; do
if [ -f $file ]; then
cp "$file" ../$3 # $3 is the third directory
fi
done
cd ../$2
for file in `ls`; do
if [ -f $file ]; then
cp -u "$file" ../$3
fi
done
My current shell script will copy files that don't exist in directory 3 already, and it won't overwrite a newer file with an older file with the same name. However, my shell script doesn't overwrite an older file with a newer file of the same name in directory 3. I don't think there's anything wrong with the -u command. Can you help find the bug in my code? Thanks!

You are missing the -u option in the first loop:
cp "$file" ../$3 # $3 is the third directory
should instead read:
cp-u"$file" ../$3 # $3 is the third directory

Related

Copy a file from a directory and paste it to multiple sub directories in linux(ubuntu) terminal?

I have a directory mnt/d/LIVI.
Inside the directory, LIVI, I have sub-directories:
mnt/d/LIVI/ak
mnt/d/LIVI/ag
mnt/d/LIVI/few
mnt/d/LIVI/ww4
mnt/d/LIVI/ks5
I wanted to copy a file named tt.txt from mnt/d/LIVI/ak/tt.txt and paste to all the sub directories of LIVI, using Ubuntu terminal. How do i do it using a shell script file?
I tried the following one, but it didn't work.
I created a text file named mnt/d/LIVI/FOLDERS.txt, This listed all the sub directories names.
And saved a script file in mnt/d/LIVI directory. The following is the script
#!/bin/sh
# -*- coding: utf-8-unix -*-
ROOTDIR=$(dirname $0 | xargs readlink -f)
for SIMDIR in cat FOLDERS.txt | xargs readlink -f ; do
cp mnt/d/LIVI/ak/tt.txt $SIMDIR
done
#cd ..
date
You may try this bash script
#!/bin/bash
cd "${0%/*}" || exit
for dir in */; do
if [[ $dir != 'ak/' ]]; then
cp ak/tt.txt "$dir"
fi
done
The script must reside under the diectory mnt/d/LIVI
Don't read lines with for.
(If you really wanted to, the syntax for that would look like
for dir in $(cat FOLDERS.txt); do
...
but really, don't. The link above explains why in more detail.)
I don't see why you want to run readlink on the directories at all?
cd "$(dirname "$0")"
while read -r dir; do
cp ak/tt.txt "$dir"
done <FOLDERS.txt
Note also Correct Bash and shell script variable capitalization

Shell script to copy one file at a time in a cron job

I have some csv files in location A like this
abc1.csv,
abc2.csv,
abc3.csv
I have a cron job which runs every 30 mins and in each execution I want to copy only 1 file(which shouldn't be repeated) and placed it in location B
I had though of 2 ways of doing this
1)I will pick the first file in the list of files and copy it to location B and will delete it once copied.Problem with this is I am not sure when the file will get copied completely and if i delete before its completed copied it can be an issue
2)I will have a temp folder.So i will copy the file from location A to location B and also keep it in temp location.In next iteration, when I pick the file from list of files I will compare its existence in the temp file location.If it exists I will move to next file .I think this will be more time consuming etc.
Please suggest if there is any other better way
you can use this bash script for your use case:
source="/path/to/.csv/directory"
dest="/path/to/destination/directory"
cd $source
for file in *.csv
do
if [ ! -f $dest/"$file" ]
then
cp -v $file $dest
break
fi
done
You can ensure you move the already copied file with:
cp abc1.csv destination/ && mv abc1.csv.done
(here you can make your logic to find only *.csv files, and not take into account *.done files.. that have been already processed by your script... or use any suffix you want..
if the cp does not succeed, nothing after that will get executed, so the file will not be moved.
You can also replace mv with rm to delete it:
cp abc1.csv destination/ && rm -f abc1.csv
Further more, you can add to the above commands error messages in case you want to be informed if the cp failed:
cp abc1.csv destination/ && mv abc1.csv.done || echo "copy of file abc1.csv failed"
And get informed via CRON/email output
Finally I took some idea from both the opted solution.Here is the final script
source="/path/to/.csv/directory"
dest="/path/to/destination/directory"
cd $source
for file in *.csv
do
if [ ! -f $dest/"$file" ]
then
cp -v $file $dest || echo "copy of file $file failed"
rm -f $file
break
fi
done

I have series of file in OLD dir, I want to check if the file exist in NEW dir. If it does not exist, I would like to do few operations on it

For instance... I have some files in the format mmddyy.zip in the OLD directory...
041414.zip
041514.zip
041614.zip
041714.zip
041814.zip(today's file Apr 18 2014)
and I have a NEW dir with 041414.zip 041514.zip in it...
I'm trying to copy all the files from OLD to NEW and do some other operations if that file doesn't exist in NEW dir...
I'm thinking of doing it ' while do' statement, but not sure what what to use in the condition...
Thanks,
Sam.
You probably want to use find, and execute a script that does both the check and the operation.
For instance, script.sh (fill in your own variables):
#!/bin/sh
FNAME="`basename $1`"
NEWDIR="/tmp"
NEWFNAME="$NEWDIR/$FNAME"
if [ ! -f "$NEWFNAME" ]; then
# do stuff to "$1"
# optionally cp "$1" "$NEWFNAME"
fi
Then you run something like cd /old/dir; find -name "*.blah" -exec script.sh "{}" ";"

linux bash script to create folder and move files

Hello I need to create folder based on a filename and in this folder create another one and then move file to this second folder
example:
my_file.jpg
create folder my_file
create folder picture
move my_file.jpg to picture
I have this script but it only works on windows and now I'm using Linux
for %%A in (*.jpg) do mkdir "%%~nA/picture" & move "%%A" "%%~nA/picture"
pause
Sorry if I'm not precise but English is not my native language.
#!/usr/bin/env bash
# Enable bash built-in extglob to ease file matching.
shopt -s extglob
# To deal with the case where nothing matches. (courtesy of mklement0)
shopt -s nullglob
# A pattern to match files with specific file extensions.
# Example for matching additional file types.
#match="*+(jpg|.png|.gif)"
match="*+(.jpg)"
# By default use the current working directory.
src="${1:-.}"
dest="${2:-/root/Desktop/My_pictures/}"
# Pass an argument to this script to name the subdirectory
# something other than picture.
subdirectory="${3:-picture}"
# For each file matched
for file in "${src}"/$match
do
# make a directory with the same name without file extension
# and a subdirectory.
targetdir="${dest}/$(basename "${file%.*}")/${subdirectory}"
# Remove echo command after the script outputs fit your use case.
echo mkdir -p "${targetdir}"
# Move the file to the subdirectory.
echo mv "$file" "${targetdir}"
done
Use basename to create the directory name, mkdir to create the folder, and mv the file:
for file in *.jpg; do
folder=$(basename "$file" ".jpg")"/picture"
mkdir -p "$folder" && mv "$file" "$folder"
done
Try the following:
for f in *.jpg; do
mkdir -p "${f%.jpg}/picture"
mv "$f" "${f%.jpg}/picture"
done
${f%.jpg} extracts the part of the filename before the .jpg to create the directory. Then the file is moved there.

How do I get the absolute directory of a file in Bash?

I have written a Bash script that takes an input file as an argument and reads it.
This file contains some paths (relative to its location) to other files.
I would like the script to go to the folder containing the input file, to execute further commands.
In Linux, how do I get the folder (and just the folder) from an input file?
To get the full path use:
readlink -f relative/path/to/file
To get the directory of a file:
dirname relative/path/to/file
You can also combine the two:
dirname $(readlink -f relative/path/to/file)
If readlink -f is not available on your system you can use this*:
function myreadlink() {
(
cd "$(dirname $1)" # or cd "${1%/*}"
echo "$PWD/$(basename $1)" # or echo "$PWD/${1##*/}"
)
}
Note that if you only need to move to a directory of a file specified as a relative path, you don't need to know the absolute path, a relative path is perfectly legal, so just use:
cd $(dirname relative/path/to/file)
if you wish to go back (while the script is running) to the original path, use pushd instead of cd, and popd when you are done.
* While myreadlink above is good enough in the context of this question, it has some limitation relative to the readlink tool suggested above. For example it doesn't correctly follow a link to a file with different basename.
Take a look at realpath which is available on GNU/Linux, FreeBSD and NetBSD, but not OpenBSD 6.8. I use something like:
CONTAININGDIR=$(realpath ${FILEPATH%/*})
to do what it sounds like you're trying to do.
This will work for both file and folder:
absPath(){
if [[ -d "$1" ]]; then
cd "$1"
echo "$(pwd -P)"
else
cd "$(dirname "$1")"
echo "$(pwd -P)/$(basename "$1")"
fi
}
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"
Some explanations:
This script get relative path as argument "$1"
Then we get dirname part of that path (you can pass either dir or file to this script): dirname "$1"
Then we cd "$(dirname "$1"); into this relative dir
pwd -P and get absolute path. The -P option will avoid symlinks
As final step we echo it
Then run your script:
abs.sh your_file.txt
Try our new Bash library product realpath-lib over at GitHub that we have given to the community for free and unencumbered use. It's clean, simple and well documented so it's great to learn from. You can do:
get_realpath <absolute|relative|symlink|local file path>
This function is the core of the library:
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
It's Bash 4+, does not require any dependencies and also provides get_dirname, get_filename, get_stemname and validate_path.
Problem with the above answer comes with files input with "./" like "./my-file.txt"
Workaround (of many):
myfile="./somefile.txt"
FOLDER="$(dirname $(readlink -f "${ARG}"))"
echo ${FOLDER}
I have been using readlink -f works on linux
so
FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)
PWD=$(pwd)
cd $DIR
#<do more work>
cd $PWD

Resources