Home > Software engineering >  mv command and rename not working on multiple flies
mv command and rename not working on multiple flies

Time:10-26

Below is a bash script to move files around and rename them. The problem is it doesn't work when there is more than one file in the directory. I'm assuming because the last parameter in the mv command is a file. Any suggestions?

'#!/bin/bash'
'INPUTDIR="/home/southern-uniontn/S001007420"'
'OUTPUTDIR="/mnt/edi-06/southern-uniontn/flats-in"'
'BACKUPDIR="/backup/southern-uniontn/S001007420"'
YEAR=`date  %Y`
MONTH=`date  %m`
DAY=`date  %d`
HOUR=`date  %H`
MINUTE=`date  %M`

######## Do some error checking #########
# Does backup dir exist?
if [ ! -d $BACKUPDIR/$YEAR ]
  then
        mkdir $BACKUPDIR/$YEAR
fi

if [ ! -d $BACKUPDIR/$YEAR/$MONTH ]
  then
        mkdir $BACKUPDIR/$YEAR/$MONTH
fi

if [ ! -d $BACKUPDIR/$YEAR/$MONTH/$DAY ]
  then
        mkdir $BACKUPDIR/$YEAR/$MONTH/$DAY
fi

if [[ $(find $INPUTDIR -type f  | wc -l) -gt 0 ]];
        then
                ###### Rename the file, move it to Backup, then copy to the Output Directory #####
                for f in $INPUTDIR/*
                do
                  echo "`date` - Move recurring txt flat file to BackupDir for Union TN from Southern"
                  mv $INPUTDIR/* $BACKUPDIR/$YEAR/$MONTH/$DAY/UnionTN-S001007420-$YEAR$MONTH$DAY-$HOUR$MINUTE.txt
                sleep 2
                  echo "`date` - Copy backup file to the Union TN Output Directory"
                  cp $BACKUPDIR/$YEAR/$MONTH/$DAY/UnionTN-S001007420-$YEAR$MONTH$DAY-$HOUR$MINUTE.txt $OUTPUTDIR/
                done;
fi

CodePudding user response:

Some notes:

  • Get out of the habit of using ALLCAPS variable names, leave those as reserved by the shell. One day you'll write PATH=something and then wonder why your script is broken.

  • mkdir -p can create parent directories, and will not error if the dir already exists

  • store the filenames in an array. Then the shell does not have to duplicate the work, and you don't need to count how many there are: if there are no files, the loop has zero iterations

  • if you want to keep the same directory hierarchy in the outputdir, you need to do that by hand.

  • use read to get the date parts

  • with bash v4.2 , printf can be used instead of calling out to date

    • use magic value "-1" to mean "now".
    • printf '%(%Y-%m-%d)T\n' -1 prints "2021-10-25" (as of the day I write this)

This is, I think, what you want:

#!/bin/bash

inputdir='/home/southern-uniontn/S001007420'
outputdir='/mnt/edi-06/southern-uniontn/flats-in'
backupdir='/backup/southern-uniontn/S001007420'

read year month day hour minute < <(printf '%(%Y %m %d %H %M)T\n' -1)

# create backup dirs if not exists
date_dir="$year/$month/$day"
mkdir -p "$backupdir/$date_dir"
mkdir -p "$outputdir/$date_dir"

mapfile -t files < <(find $inputdir -type f)
for f in "${files[@]}"
do
    ###### Rename the file, move it to Backup, then copy to the Output Directory #####
    backup_file="UnionTN-S001007420-$year$month$day-$hour$minute.txt"

    printf '%(%c)T - Move recurring txt flat file to backupdir for Union TN from Southern\n' -1
    mv "$f" "$backupdir/$date_dir/$backup_file"

    printf '%(%c)T - Copy backup file to the Union TN Output Directory\n' -1
    cp "$backupdir/$date_dir/$backup_file" "$outputdir/$date_dir/$backup_file"
done

CodePudding user response:

When using a glob with mv, the target must be an existing directory, and all matching files will be moved inside that directory.

In your case,

mv $INPUTDIR/* $BACKUPDIR/$YEAR/$MONTH/$DAY/UnionTN-S001007420-$YEAR$MONTH$DAY-$HOUR$MINUTE.txt

tells mv to move all file inside the $INPUTDIR/* directory to a directory named $BACKUPDIR/$YEAR/$MONTH/$DAY/UnionTN-S001007420-$YEAR$MONTH$DAY-$HOUR$MINUTE.txt.

I'm not sure what you're trying to do, but I hope this help.

Some more advice you could use:

  • you don't have to put the shebang (the first line beginning with "#") and the first three variable declarations inside single-quotes.
  • Some argue it is more portable and better to write /usr/bin/env bash instead of /bin/bash in the shebang
  • if [ CONDITION ] /then ACTION /fi statements can be simplified by writing [ CONDITION ] && ACTION
  • You reduce your likely hood of encountering unexpected behaviour when using string interpolation (i.e. write "${year}/${month}/" instead of ${year}/${month}.
  • No need to call mkdir a, followed by mkidr a/b, then mkdir a/b/c and so on, you can just call mkdir -p a/b/c. The p flag tells mkdir to create parent directories if they don't already exist.
  • It is unnecessary to validate the existence of a directory before calling mkdir since mkdir already validates that for you.
  • As pointed out by commenters, all-caps variables are conventions for special POSIX related variables. You should use another type of casing.
  • You could use date to do the formatting for you: date %Y/%m/%d will print 2021/10/25
  • Strings without interpolation can have single-quotes.
  • (Optional, prevent undesired behaviors) Put set -e at the beginning of your scripts, after the shebang, to tell bash to halt if an error is encountered
  • And finally, use man <command_name> for built-in documentation!
  •  Tags:  
  • bash
  • Related