1

This may be a duplicate of Duplicate file x times in command shell and is definitely a duplicate of How to duplicate a file a number of times while embedding an index in each file but the person who posted the answer was last seen in 2017 and I'd like to know how I can use this as a function in zsh so that I can call it on a file with any extension (not just txt files) like so: cpx file.ext n where n is the number of copies to make. Also, how I can separate the filename and file extension.

This was the answer for txt files only:

#!/bin/sh

orig=ascdrg3.txt # start with this file

in=$orig
count=1 #loop variable
max=5   #number of files to create
while test "$count" -le "$max" ; do
    # Remove extension
    base=$(basename "$in" .txt)

    # get the prefix
    prefix=$(expr substr "$base" 1 $((${#base}-1)))

    # get last letter
    last=$(expr substr "$base" ${#base} 1)

    while true ;
    do
        # Advance letter, while the file doesn't exist
        last=$(echo "$last" | tr A-Z B-ZA)
        last=$(echo "$last" | tr a-z b-za)
        last=$(echo "$last" | tr 0-9 1-90)

        # construct new file name
        new="$prefix$last.txt"

        # continue if it doesn't exist
        # (otherwise, advance the last letter and try again)
        test -e "$new" || break

        test "$new" = "$orig" \
            && { echo "error: looped back to original file" >&2 ; exit 1; }
    done


    # Create new file
    cp "$orig" "$new"

    # Modify first line of new file
    sed -i "1s/\$/number($count,$max)/" "$new"

    # Advance counter
    count=$((count+1))

    # loop again
    in=$new
done

Is there a much smaller way to do this?

What I want is: cpx hello.py 3 should create hello1.py hello2.py hello3.py

ntruter42
  • 187
  • 9

1 Answers1

2

There's definitely a simpler way to do it robustly in zsh. There's a simpler way to do it robustly in plain sh, too: this script is overly complicated and fragile (assumes all file names have an extension, overwrites files without prompting, …). Since this thread is about zsh, I'll take advantage of zsh's capabilities.

The history and parameter expansion modifiers r and e are useful to split a file name between the base name and the extension. However, beware that they only work usefully when the file does have an extension.

Warning: untested code.

function cpx {
  if (($# != 2)); then
    cat >&2 <<EOF
Usage: cpx FILENAME N
Make N copies of FILENAME.
EOF
    return 1
  fi
  local n=$2
  if [[ $n != <-> ]]; then
    print -ru2 "cpx: $n: not a number"
    return 1
  fi
  local prefix=$1 suffix= i
  # If there is an extension, put the number before the extension
  if [[ $prefix:t == ?*.* ]]; then
    prefix=$1:r
    suffix=.$1:e
  fi
  # If the part before the number ends with a digit, separate the additional
  # number with a dash.
  if [[ $prefix == *[0-9] ]]; then
    prefix+="-"
  fi
  # Copy foo.bar to foo1.bar, foo2.bar, ...
  for ((i=1; i<=n; i++)); do
    cp -p -i -- $1 $prefix$i$suffix
  done
}
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • I tested it with many differently named files (eg. file, file.txt, file.$1, file-2, file.3.ext, folder/, etc) and it didn't break so it works perfectly! Just when I used a math expression `cpx file 5+1` then it printed `cpx: 5+1: not a number` but it still did the job successfully. – ntruter42 Apr 03 '21 at 22:47
  • What does the `local prefix=$1 suffix= i` do? I removed the `i` and just left `local prefix=$1 suffix= ` and it still works. Is that `i` necessary? – ntruter42 Apr 03 '21 at 23:03
  • 2
    @ntruter42 Without `local`, the variables will remain defined after the function is executed. If you want to be able to perform arithmetic expressions, you can use `typeset -i i` instead of `local i`. – Gilles 'SO- stop being evil' Apr 03 '21 at 23:08