I have 19 directories (0, n10, n20, n30, ... n180) under the folder "mini". I need to copy two files from each directory (0_mini_vh1.coor and 0_mini_vh1.xsc, n10_mini_vh1.coor and n10_mini_vh1.xsc, so on) into their corresponding directories with the same name (0, n10, n20, so on) under the folder "production". So far, I have this written for a script, but it doesn't seem to be working. How should I improve the script?

#!/usr/bin/env bash
for f in n10  n100  n110  n120  n130  n140  n150  n160  n170  n180  n20  n30  n40  n50  n60  n70  n80  n90
cd $f
echo "cp $f_mini_vh1.coor ../../production/$f"
echo "cp $f_mini_vh1.xsc ../../production/$f"
cd ../
  • if the target directory is always before the first _, you can retrieve its name via f=n10_mini_vh1.coor; echo ${f%%_*} – Rorschach Jul 1 '19 at 21:31
for f in n{10..180..10}; do (
    cd "$f"
    echo cp "$f"_mini_vh1.{coor,xsc} ../../production/"$f"
) done

You can use curly braces to expand to all of the desired names: {start..stop..incr}. You can use the same trick to combine the two file names with {coor,xsc}.

Be careful writing $f_mini_vh1. That's a variable called f_mini_vh1. To separate f from the rest you need to write either ${f}_mini_vh1 or "$f"_mini_vh1. I recommend the latter to accord with general guidance that one always quote variable expansions.

A subshell with parentheses will save you from having to cd ../ each time. I like to use a subshell any time I use cd in a script to limit the scope of the directory change.

You could also just skip the cd-ing entirely by passing adding $f to the cp command:

    echo cp "$f"/"$f"_mini_vh1.{coor,xsc} ../production/"$f"
| improve this answer | |
  • I ran the script you listed first, but it came up with these errors: ./copy.sh: line 4: syntax error near unexpected token vh1.{coor,' ./copy.sh: line 4: echo cp "$f"_mini)vh1.{coor, xsc} ../../production/"$f"' – Jillian Jul 1 '19 at 21:51
  • Don't put a space after the comma. – John Kugelman Jul 1 '19 at 22:27

This could also be done with an array and a while loop!

This is the way I would do it:

arr=(0 n10  n100  n110  n120  n130  n140  n150  n160  n170  n180  n20  n30  n40  n50  n60  n70  n80  n90)
while [ $cnt -lt ${#arr[@]} ]; do
cp ./$f/${f}_mini_vh1.xsc ../production/$f
cp ./$f/${f}_mini_vh1.coor ../production/$f
let cnt=cnt+1

But I'm still relatively new and always learning and John's method seems cleaner, but I wanted to offer an alternative :) This should work too.

| improve this answer | |

Your Answer

