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.

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!

  • why don't use mv -v instead of echo ? – Alexander Jan 11 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 at 12:48
  • Firstly, thank you for your comments and apologies for the delay in reply. – DavRog Jan 22 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 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 at 11:28
0

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

prename 's/\.xfr$// and print "$_ resended\n"' ~/test/*  >  f.log

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.