1

Very new to scripting. I have a directory containing some text files and some with text files that also contain an additional extension (.xfr) e.g. file1.txt, file2.txt.xfr, file3.txt, file4.txt.xfr.

Trying to write a bash script to check the directory, rename the file if it contains the .xfr (removing the extension) and then write out log entries depending on the outcome.

I'm able to write a script to rename files but when it comes to the if part, I'm struggling. Here is the bit that works:

#!/bin/bash

for file in ~/Test/Files/*.xfr
do
    mv "$file" "${file%.xfr}"
    echo "$file has been resent" > ~/Test/log.txt
done

To add the if, I thought this would work but sadly not:

#!/bin/bash

if [[ ! -e ~/Test/Files/*.xfr ]]
then
    mv "$file" "${file%.xfr}"
    echo "$file has been renamed" > ~/Test/log.txt
else 
    echo "no files were renamed" ~/Test/log.txt
fi

Any assistance would be greatly appreciated.

Update: the script was updated to use varibales in place of the file path and so now looks like this:

#!/bin/bash

PATH='/home/davrog/Test/Files'
LOG='/home/davrog/Test/log.txt'

for file in $PATH/*.xfr

do

if [ -e $PATH/$file ]
   then
     mv "$file" "${file%.xfr}"
     echo "$(date) $file has been resent"
   else
     echo "$(date) no files were stuck when check was run"
fi

done >>$LOG

This seems to have broken the mv function, I suspect because it is trying to rename the file back it it's current name. Looking at other posts, the mv function looks be be correct, so not sure how I can resolve that. Thanks in advance for any assistance.

ilkkachu
  • 138,973
DavRog
  • 11
  • 3

2 Answers2

2

Modifying your first script a tiny bit:

#!/bin/bash

for file in ~/Test/Files/*.xfr
do
    if [ ! -e "$file" ]; then
        echo 'no files were renamed'
        break
    fi

    mv "$file" "${file%.xfr}"
    echo "$file has been renamed" 
done > ~/Test/log.txt

I've done two things here:

  1. The redirection to the log file would truncate the log file in each iteration, so I've moved it to the end of done instead so that it redirects all standard output of the loop.

  2. If the pattern does not match any filenames, it will remain unexpanded by default. I detect this with a -e test and output a special message. Then I break out of the loop.

Alternative:

#!/bin/bash

for file in "$HOME/Test/Files"/*.xfr
do
    if [ -e "$file" ]; then
        mv "$file" "${file%.xfr}"
        printf '%s has been renamed\n' "$file"
    else
        echo 'no files were renamed'
    fi   
done >"$HOME/Test/log.txt"

Here I've just changed the flow a bit and used $HOME in place of tilde (looks nicer in a script IMHO). I'm also using printf instead of echo as it's generally safer for outputting variable data (see e.g. "Why is printf better than echo?").

Both alternatives above may also run with /bin/sh rather than /bin/bash.

Another alternative, if you want to write more of a "report":

#!/bin/bash

shopt -s nullglob

xfr_files=( "$HOME/Test/Files"/*.xfr )

for file in "${xfr_files[@]}"; do
    mv "$file" "${file%.xfr}"
done

printf '%d files renamed\n' "${#xfr_files[@]}"
if [ "${#xfr_files[@]}" -gt 0 ]; then
    printf '\t%s\n' "${xfr_files[@]}"
fi

This sets the nullglob shell option in bash which makes patterns expand to nothing if there are no matches. It then writes out the number of files renamed and if this number is greater than zero, it also lists the filenames with a tab indent.


In your updated question, you use the variable PATH. This happens to be the variable that holds the search path for shell utilities, and setting it to anything other than a :-delimited list of directories will likely make the shell unable to find mv and other common tools.

In general, avoid using uppercase variable names in shell scripts, and instead use lowercase or possibly mixed-case variables. This way, you avoid accidentally overriding important variables that the shell may use.

Additionally, the [ -e $PATH/$file ] test should read [ -e "$file" ] as the path is already included in the value of $file. This is also what I used in my code above. Don't forget to double-quote all variable expansions!

Kusalananda
  • 333,661
  • why don't use mv -v instead of echo ? – Alexander Jan 11 '19 at 12:44
  • @Alexander Two reasons: 1) I prefer writing solutions that are portable (-v is a non-standard option for mv), and 2) The user may want to output a custom text for each file, as they do in the question. – Kusalananda Jan 11 '19 at 12:48
  • Firstly, thank you for your comments and apologies for the delay in reply. – DavRog Jan 22 '19 at 11:14
  • Firstly, thank you for your comments and apologies for the delay in reply. The mv operation - mv "$file" "${file%.xfr}" - was working fine until I added variables for the the file path location. Would that need to change now the directory path and filename is not hard-coded? – DavRog Jan 22 '19 at 11:20
  • @DavRog Please update your question with what you currently are attempting to do and I'll update the answer accordingly. – Kusalananda Jan 22 '19 at 11:28
  • @Kusalananda - thanks for checking in again, I have updated the question, hopefully makes sense. Apologies for my ignorance, all very new to this – DavRog Jan 22 '19 at 11:57
  • @DavRog See addition to answer. – Kusalananda Jan 22 '19 at 12:04
  • Thanks again @Kusalananda. Comments understood and renaming the variables certainly fixes the errors that were occurring but it doesn't actually rename the files. As I mentioned, the mv command was working fine before I added the variables so it's possible I have inadvertently changed something else that prevents it from removing the .xfr extension. – DavRog Jan 22 '19 at 12:17
  • @DavRog Ah, I see it now. The variable $file will contain the whole path to the file, not just the filename. This is good and proper, but it also means that you don't have to prepend the path in the [ -e ... ] test. That should be just [ -e "$file" ] (as in my code). – Kusalananda Jan 22 '19 at 12:18
  • Thanks so much, really appreciate your help! – DavRog Jan 22 '19 at 12:42
0

If you have prename istalled you could run something along the lines of

prename 's/\.xfr$// and print "$_ resended\n"' ~/test/*  >  f.log
JJoao
  • 12,170
  • 1
  • 23
  • 45