1

I am currently writing a bash script to automate a task for my research, i.e., procedurally creating multiple scripts that will be sent to a cluster.

My bash script is composed of a copy operation that will copy all the files to some folder with a name generated with a procedure, except this one file. I know how to do this manually, but within the bash script it does not seem to work:

#!bin/sh

i=0
var=""

while [ $i -lt 6 ]
do
   i=$(( $i+1 ))
   
   var="NameOfFolder${i}"
   mkdir "${var}"
   cp !(fileName) "${var}"
done

The problem I am having is that the parentheses in cp command are being interpreted differently.

The error I get is:

Syntax error: "(" unexpected (expecting "done")

When I remove the line in which the cp command is given the script runs, so the problem is in that line. I have already tried:

#!bin/sh

i=0
var=""

while [ $i -lt 6 ]
do
   i=$(( $i+1 ))
   
   var="NameOfFolder${i}"
   mkdir "${var}"
   cp !((fileName)) "${var}"
done

and

#!bin/sh

i=0
var=""

while [ $i -lt 6 ]
do
   i=$(( $i+1 ))
   
   var="NameOfFolder${i}"
   mkdir "${var}"
   cp ( !(fileName) ) "${var}"
done

Without any success.

Question

What is the proper way to introduce the commands in parentheses without the bash script interpreting them as "programming" operators?
13
  • 2
    !() is an extended pattern matching operator used by bash extglob. If you switch to #!/bin/bash and add shopt -s extglob your first example will work
    – user986805
    Feb 16, 2021 at 16:49
  • it should be mkdir and instead of i=$((i+1)) you can just type ((i++)) ... there is no reason to set the var two time...
    – user986805
    Feb 16, 2021 at 16:53
  • Sorry, did not notice the mkdir typo in the post; just fixed it. Even if I change to the #! bin/bash and add the shopt -s extglob it still displays the same error.
    – user1173662
    Feb 16, 2021 at 16:55
  • 1
    Thanks @sudodus, the command that worked was: rsync * --exclude $fileName "./${var}", thanks a lot. Please answer the question, this might help others. Although I am still curious as to why shopt does not come with my Ubuntu 20.04 Linux distribution(!?)
    – user1173662
    Feb 16, 2021 at 17:27
  • 1
    Since shopt is not found, I don't think you are using Bash. Your script calls /bin/sh which is probably (a symlink to) the dash shell. Dash has no shopt.
    – Zanna
    Feb 17, 2021 at 8:45

3 Answers 3

2

You can use brace expansion to create a range of folders (you will also need to exclude the folders themself)

#!/bin/bash

shopt -s extglob

for i in folder_{1..6}; do
    mkdir -p "$i" && \
    cp -t "$i" -- !(filename|${i%_*}_*)
done
8
  • Still get the error message: shopt: not found, even with the type shopt directive.
    – user1173662
    Feb 16, 2021 at 17:42
  • what does realpath /proc/$$/exe say?
    – user986805
    Feb 16, 2021 at 17:46
  • It says /usr/bin/dash
    – user1173662
    Feb 16, 2021 at 17:48
  • make bash my default shell
    – user986805
    Feb 16, 2021 at 17:53
  • Followed the advice and still says that /bin/bash is the default, so I hit enter (or type it in). I run again the realpath /proc/$$/exe command and it throws the same message: /usr/bin/dash.
    – user1173662
    Feb 16, 2021 at 18:00
1

You can use a rsync as an alternative to the construct with cp: replace for example

cp ( !(fileName) ) "${var}"

with

rsync --exclude="$fileName" * "./${var}"

The --exclude option of rsync makes things easy to manage.

1
  • I like your solution, works better when remote copying than regular cp.
    – user1173662
    Feb 16, 2021 at 20:07
0

Hello, welcome to Askubuntu!

Try this:

Where:

  1. find - a tool to (from man) "search for files in a directory hierarchy"
  2. . is the folder/directory you want to include (all sub-directories & sub-files under this one will be searched)
  3. -name *.ext - this is the extension (the ending .name of the file)
  4. -type f is a type file.
  5. -exec strings {} path/to/this/copiedfiles \; - this one is executing the command cp on file 1,file 1..., all up to last file found to path/to/this/copiedfiles

So, for example:

Copy all files ending with ".odt" in the folder /home/bob/docs/ (including all the sub-files in sub-directories under docs/) to the directory /home/bob/backups/odt/:

find /home/bob/docs/ -name "*.odt" -type f -exec cp {} /home/bob/backups/odt/{}.backup \;

1
  • As usual - you reading this message; (first of all, I hope you are fine, and have a great, corona-free year!) - I want to (however possible - in any way) improve my answers, so any feedback/edit/critic is well appreciated! (Does not need to sound nice - We are here to improve) Have it Great, //Wishes from Sweden! Feb 16, 2021 at 19:26

Your Answer

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