views:

50

answers:

4

Hi, I want to execute following command in shell script

cp /somedire/*.(txt|xml|xsd) /destination/dir/

But this does not run inside shell script. Any quick help?

createjob.sh: line 11: syntax error near unexpected token `('

My shell is zsh.

Thanks Nayn

A: 

No idea about zsh but Bash doesn’t know about regular expressions in paths, only wildcards.

You can try using find:

find -E . -regex '.*\.(txt|xml|xsd)' -exec cp {} /destination/dir \;

Have a look at the manpage for an explanation of the syntax of find.

Konrad Rudolph
`*.(txt|xml|xsd)` is a shell pattern in zsh, and it means what you think. Note that `-E` is not standard (it seems to be specific to FreeBSD). Your command differs from the original in that it recurses into subdirectories.
Gilles
@Gilles: `-E` is indeed nonstandard but using the Unix commands (`sed`, `grep` …) without it is a real pain in the *ss because all you get is a regex version from the last millenium.
Konrad Rudolph
A: 

You can't use regex's in the SOURCE like that with CP in most instances (maybe shell specific)

#!/bin/sh
cp /somedire/*.txt /destination/dir/
cp /somedire/*.xml /destination/dir/
cp /somedire/*.xsd /destination/dir/
Rudu
+2  A: 

Your use of parentheses and alternation is a zsh-specific construct. It doesn't work in other shells, including zsh in sh compatibility mode.

If you want to keep using this construct, you'll have to invoke zsh as zsh (presumably by replacing #!/bin/sh by #!/bin/zsh or something like that).

If you need your script to run on ksh, use #!/bin/ksh or #!/usr/bin/env ksh and

cp /somedire/*.@(txt|xml|xsd) /destination/dir/

If you also need to support bash, that same command with the @ will work provided you run the following commands first:

shopt -s extglob 2>/dev/null  ## tell bash to parse ksh globbing extensions
setopt ksh_glob 2>/dev/null   ## tell zsh to parse ksh globbing extensions

If you need POSIX sh compatibility, you'll have to use three separate commands, and prepare for an error message if any of the three extensions has no match. A more robust solution would use find:

find /somedire -name /somedire -o -type d -prune -o \
     \( -name '*.txt' -o -name '*.xml' -o '*.xsd' \) \
     -exec sh -c 'cp "$@" "$0"' /destination/dir {} +
Gilles
A: 

This would work in bash, and probably zsh as well: cp /somedire/*.{txt,xml,xsd} /destination/dir/

It's not in POSIX, though, so it won't work with most /bin/sh's.

Jonas Wagner
It will work if and only if there are and txt, and xml, and xsd files, while `(|)` version will fail only if there are no files with such extensions.
ZyX
So one works if there _are_ files, the other fails if there are no files... I don't understand the difference you're pointing out.
Jonas Wagner
@Jonas Wagner your command should fail if there are, for example, *.txt and *.xml files, but no *.xsd files. Depending on shell options it may leave `/somedire/*.xsd` pattern (like bash), nothing (in this case it works like `(|)` version) (with `nullglob` option) or refuse to run a command with `no matches found` error (default for zsh).
ZyX