-1

I'm trying to run this script:

mkdir a
cd a
touch a1, a2, a_
ls ./a!(_)
echo -e '#!/bin/bash\nls ./a!(_)\n' >> run.sh
chmod a+x run.sh
./run.sh

The ls line prints the output as expected (./a1, ./a2,), but the script fails with:

./run.sh: line 2: syntax error near unexpected token `('
./run.sh: line 2: `ls ./a!(_)'

Is there any way to use parentheses in a bash script without using find or for?

Guin Ness
  • 21
  • 4
  • [here?](https://unix.stackexchange.com/questions/494561/how-to-escape-parenthesis-in-bash-rmdir) – vsh Dec 24 '19 at 18:54
  • @GuinNess, ...btw, consider avoiding `.sh` extensions, *particularly* for bash scripts (which are not `sh` scripts). In general, UNIX commands don't have extensions (you run `ls`, not `ls.elf`; similarly, while a Python *library* will end in `.py`, a Python *script* installed in `/usr/bin/` or such typically won't). – Charles Duffy Dec 24 '19 at 20:21

1 Answers1

3

You need to enable bash's extglob option with shopt -s extglob to use the negation pattern.

Change the echo line to

printf '%s\n' '#!/bin/bash' 'shopt -s extglob' 'ls ./a!(_)' > run.sh

You can disable extended globbing afterwards with shopt -u extglob.

Related:

Freddy
  • 4,548
  • 1
  • 7
  • 17
  • 2
    `echo -e` is innately unreliable -- whether it works (even in bash) depends on both compile-time and runtime-configurable options (`shopt -s extglob` when `set -o posix` is active, and `echo -e` will emit `-e` on output; either environment variables or compile-time flags can do the same). Using `printf` is much more reliable (working not just on all bash configurations, but also with standards-compliant `/bin/sh` implementations). – Charles Duffy Dec 24 '19 at 19:36
  • 1
    In this case: `printf '%s\n' '#!/bin/bash' 'shopt -s extglob' 'ls ./a!(_)'`; easier-to-read, too, since each line becomes one argument. – Charles Duffy Dec 24 '19 at 19:38
  • @CharlesDuffy Agreed, replaced `echo` with `printf`. – Freddy Dec 24 '19 at 19:43