0

I want to know how can I copy or shell copy multiple files within a bash script. What I mean is that

cp /path/to/source/{file1,file2,file3} /path/to/dest

and

scp /path/to/source/{file1,file2,file3} user@host:/path/to/dest

will work fine but as example

#!/bin/sh
scp /path/to/source/{file1,file2,file3} user@host:/path/to/dest

will throw an error like this:

/path/to/source/{file1,file2,file3}: No such file or directory

If you will copy or shell copy a single file it works, so the problem are multiple files. Also it works if I would use * for all files but I do not want to copy all files. I should only copy selected files because in both folders are files with the same name but their content is different. Thereby to copy all files and then remove the not needed files would not work.

For better understanding following would work:

#!/bin/sh
scp /path/to/source/file1 user@host:/path/to/dest

Also following:

#!/bin/sh
scp /path/to/source/* user@host:/path/to/dest

So it has something to do with the correct use of { ... } for multiple files which will work inside the terminal but not if I run the bash script in it.

Thanks in advance.

//Edit:

I add the error if I try it with cp:

cp: cannot stat '/path/to/source/{file1,file2,file3}': No such file or directory
Micha93
  • 117
  • Could you provide an output of ls -l /bin/sh? – BlueManCZ Feb 23 '21 at 10:55
  • What happens if you run the script with bash script.sh instead of ./script.sh? – BlueManCZ Feb 23 '21 at 10:59
  • I retrieve lrwxrwxrwx 1 root root 4 Apr 23 2020 /bin/sh -> dash. If I run it with bash script.sh instead of ./script.sh it does not change anything. I will get the same errors. But a good point you raised there. – Micha93 Feb 23 '21 at 11:01
  • What's funny is that I can copy the path of the error message and run it in the terminal without any problems. So the {} are output in the error message, only he does not perform it accordingly. I feel extremely stupid because I am missing some very simple trivial part. I'm really doubting myself right now. – Micha93 Feb 23 '21 at 11:04
  • Well, it is interesting. I just cannot reproduce it. Looks like some configuration-specific thing. – BlueManCZ Feb 23 '21 at 11:06
  • Okay, I can reproduce it. With dash script.sh. Once more, what is the output of ls -l /bin/bash? – BlueManCZ Feb 23 '21 at 11:09
  • And bash --version? – BlueManCZ Feb 23 '21 at 11:10
  • -rwxr-xr-x 1 root root 1113504 Jun 7 2019 /bin/bash and `GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html

    This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.`

    – Micha93 Feb 23 '21 at 11:11
  • 1
    Change sh to bash brace expansion is not posix that could be the problem – Jason Croyle Feb 23 '21 at 11:18
  • 3
    Does this answer your question? Why is brace expansion not supported? Also https://unix.stackexchange.com/q/192464 – Quasímodo Feb 23 '21 at 11:40

1 Answers1

6

You have #!/bin/sh in your script which means it will be run by sh, not bash. On many Debian-derived systems, Ubuntu for example, /bin/sh is a symlink to the basic POSIX shell dash. The brace expansion you are using is not supported by dash:

$ dash
$ echo {foo,bar}
{foo,bar}

This means that the command cp /path/to/source/{file1,file2,file3} /path/to/dest is looking for a file called {file1,file2,file3}. The simple solution is to use bash instead. Just change your shebang line from #!/bin/sh to #!/bin/bash and you should be fine.

terdon
  • 242,166
  • 1
    Sounds logical, you're certainly right, but unfortunately it doesn't help. – Micha93 Feb 23 '21 at 11:25
  • @Micha93 please edit your question and i) tell us your operating system, ii) show us the exact command you use to run the script, iii) add the output of ls -l /bin/bash and /bin/bash --version. Please don't put it in comments, add to the main body of the question (and no need to add "EDIT", just edit so it fits in the question naturally). The braces work fine in bash scripts, just not sh scripts. – terdon Feb 23 '21 at 11:32
  • 1
    okay, I detected my problem but I am too stupid to solve it. What would work is /path/to/source/{file1,file2,file3}, sorry my fault. What I tried to do is FILES=file1,file2,file3 and then I tried /path/to/source/{$FILES}. So as output I get the correct "String" but of course {$VARIABLE} is not the same as that I want to fill inside of it the variable. – Micha93 Feb 23 '21 at 11:41
  • 1
    @Micha93 ah yes, that's a completely different situation to what you have in your question. You can't use variables inside {} brace expansions because the braces are expanded before the variable is evaluated. I suggest you ask a new question, showing the actual code you are using and explaining what you want to achieve with it. We should be able to give you a workaround. – terdon Feb 23 '21 at 11:45
  • No thank you. Now I have the solution. I have to run eval "cp /path/to/source/{${FILES}} /path/to/dest" – Micha93 Feb 23 '21 at 11:52
  • @Micha93 If this solves your problem, please accept it. I see you have accepted no answers up to now so this is meant as a reminder. – Quasímodo Feb 23 '21 at 11:54
  • 5
    @Micha93 oh no, please don't do that, it's a completely unnecessary security hole, needlessly complex code and very fragile (it will break if one of your files has anything as simple as a space in it). It's also quite pointless, you could use an array and avoid the entire problem. Please post a new question. – terdon Feb 23 '21 at 11:56
  • To others reading this: eval should NOT be used in this situation, for all the reasons mentioned by @terdon in their earlier comment (fragile, opens a possibly security hole for no reason, and extra complexity for the next person who has to read it) – JonathanDavidArndt Feb 13 '23 at 04:39