0

I want to copy many files inside a single directory. An example filename starts with error042021.mysite and I want the new name end with -dev.co.at.txt. I think I need a wildcard for the number, because the numbers will differ: 022021, 032021, 042021, etc. (number= Month, Year).

For example this is what I want to do with error042021.mysite.at.txt:

cp -p error042021.mysite.at.txt error042021.mysite-dev.co.at.txt

I don't wan't to use mv because the old file should still remain.

I tried:

FILE=/srv/www/mysite.at/storage/logs/error/error??????.mysite.at.txt
if [[ -f "$FILE" ]]; then
    echo "$FILE exists."
    cp -p error??????.mysite.at.txt error??????.mysite-dev.co.at.txt
fi

Or is it better to use sed?

1 Answers1

0

There are few problems with your code.

  • FILE=/srv/www/mysite.at/storage/logs/error/error??????.mysite.at.txt does not trigger filename generation. If it did, you could get more than one result. There is no straightforward way to store more than one pathname in a variable and be able to reliably retrieve pathnames one by one.

    In Bash you can use an array:

    FILE=( /srv/www/mysite.at/storage/logs/error/error??????.mysite.at.txt )
    

    This triggers pathname generation and allows to retrieve pathnames reliably. Arrays are useful in general but in this case not necessary. To iterate over the results of expansion you don't need an array variable, you can use the pattern directly in a for loop:

    for f in /srv/www/mysite.at/storage/logs/error/error??????.mysite.at.txt; do …
    

    Arrays are not portable, using patterns in with for is.

    Your [[ -f "$FILE" ]] looks like an attempt to see if the file exists. If not the above problem, this would be a good idea. Inside our for f in … loop we need something similar because error??????.mysite.at.txt may match nothing. If it matches nothing then it will stay as it is (with literal ? characters). There are few ways to deal with this:

    • In Bash shopt -s nullglob invoked before using a pattern will make the pattern expand to a null string if there is no match. In this case, if the pattern is used in for like above then the inside of the loop will be executed zero times, as you would expect.
    • Or you can test inside the loop if $f is not the literal pattern used.
    • Or you can test inside the loop if $f is a pathname to some existing file. In principle this is what you tried to do with [[ -f "$FILE" ]]. I prefer [ over [[ wherever [ is enough, because [ is portable and [[ is not.

    There may be other reasonable ways, some of them specific to what you want to do. E.g. you can not care and let cp fail (possibly silently: 2>/dev/null cp …).

  • FILE is not a good name for a variable. If you get used to uppercase names then sooner or later you will overwrite your PATH. In a script use lowercase names in general: file, f, path (not in zsh though), …

  • When parsing cp -p error??????.mysite.at.txt error??????.mysite-dev.co.at.txt, the shell will expand each pattern separately. There is no connection whatsoever between the first and the second occurrence of ??????. Assuming the script is in the right directory and the first pattern expands to something, the second pattern will most likely stay as-is (or disappear if nullglob is active) because there is no matching file.

    The first pattern may expand to multiple pathnames in the first place. In some circumstances the second pattern may expand to one or more pathnames.

    Inside our for loop the source pathname is just $f (and we will quote properly). We need to craft a proper destination pathname. In general sed may be the right tool to manipulate pathnames; still in this case the shell is enough, like in this other answer of mine. We will get the proper destination pathname from the expansion of ${f%.at.txt}-dev.co.at.txt.

Our basic code is:

#!/bin/sh
for f in /srv/www/mysite.at/storage/logs/error/error??????.mysite.at.txt; do
    [ -f "$f" ] && cp -p -- "$f" "${f%.at.txt}-dev.co.at.txt"
done

Notes:

  • I used /bin/sh as the shebang because the code is fully portable.
  • I used the absolute path when generating pathnames, so there is no need for -- while invoking cp. Still I decided to put it there to promote good practice. Even if you choose to cd /…/error first and for f in error??????.mysite.at.txt; do …, then the string from the expansion of $f will never start with - and the existence of -- will change nothing. But if someone takes this code and uses it with a pattern like ??????.mysite.at.txt then they may really need --. So here you are, -- is already in the right place.