2

I am writing a bash script to change a string

./FirstJavaProgram.class

to

FirstJavaProgram

I came up with this script. However bash complains line 4 is bad substitution.

#!/bin/sh
file=${1%%.class}
echo $file
file=${file/.\//}

How should I write the correct syntax?

drdot
  • 3,215
  • 9
  • 46
  • 81
  • `file="${file/.\//}"` You can prove it to yourself with `file="./FirstJavaProgram.class"; echo "${file/.\//}"` (note: since you are not replacing with anything, `"${file/.\/}"` -- the closing `'/'` is presumed) Also in bash `file="${file:2}"` will do it as well. – David C. Rankin Apr 09 '21 at 05:41
  • You can also use `"${file#*/}"` If your string is outside the script, you don't need a script at all, e.g. `echo "./FirstJavaProgram.class" | sed 's|^[.]/||'` – David C. Rankin Apr 09 '21 at 05:46
  • 1
    @drdot : `basename "$file" .class` – user1934428 Apr 09 '21 at 06:03
  • `#!/bin/sh` does not run your script with **bash**. `sh` is a different language, with fewer features (including, less available PE syntax). Use `#!/usr/bin/env bash` to use a copy of bash found with a PATH lookup, or `#!/bin/bash` to use a copy of bash from `/bin` if you're sure that's the one you want. – Charles Duffy Apr 20 '21 at 22:34
  • @user1934428, ...starting the external program `basename` is a lot slower to run than the built-into-the-shell PE approach the OP asked for. Compare `time for ((i=0; i<100; i++)); do file=$(basename "$file" .class); done` with `time for ((i=0; i<100; i++)); do file=${file##*/}; file=${file%.class}; done`. I measure 100 `basename`s as taking more than half a second, whereas the PE loop finishes in under 1/100th of a second total. – Charles Duffy Apr 20 '21 at 22:39
  • One way, quick, simple but not the safest (...) , is simply to copy a launcher script, or even the script itself in `/usr/bin`, while taking good care of dependencies paths. I do this since years, not a problem to me. By doing so, you can then call your programs from other script and build complex stuffs. – NVRM Apr 20 '21 at 22:40
  • @drdot, ...and in the future, use the `sh` tag for questions about `#!/bin/sh` scripts; the bash tag should only be used for bash scripts. – Charles Duffy Apr 20 '21 at 22:41
  • @DavidC.Rankin : Your solution just removes the path prefix, but not the extension `.class`. – user1934428 Apr 21 '21 at 06:30
  • @user1934428 `echo "./FirstJavaProgram.class" | sed -e 's|^[.]/||' -e 's/[.].*$//'` – David C. Rankin Apr 21 '21 at 06:40

2 Answers2

1

${var//search/replace} is a bash-only feature. It is not guaranteed to be present with /bin/sh. Use ${var#prefix} instead, which is part of the POSIX sh specification and so guaranteed to be offered by /bin/sh.

#!/bin/sh
file=${1%.class}
echo "Trimmed suffix:      $file"
file=${file#./}
echo "Also trimmed prefix: $file"

...if the parameter is ./FirstJavaProgram.class, the output will be:

Trimmed suffix:      ./FirstJavaProgram
Also trimmed prefix: FirstJavaProgram

By contrast, if you want to use bash-only features, start your script with #!/usr/bin/bash, or #!/bin/bash, #!/usr/bin/env bash, etc. as appropriate.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
0

Try this command: echo "./FirstJavaProgram.class" | sed 's\[.]/\\'

Your script should be look like:

#!/bin/sh
file=./FirstJavaProgram.class
echo $file 
echo $file | sed 's\[.]/\\'
Mikołaj Głodziak
  • 4,775
  • 7
  • 28
  • 1
    When a question asks for a PE (which is fast and efficient, requiring no tools outside of bash itself), answering with `sed` (an external binary that takes time to start up) is not ideal. – Charles Duffy Apr 20 '21 at 22:33
  • (Also, this has the bugs discussed in [I just assigned a variable, but `echo $variable` prints something else!](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else)) – Charles Duffy Apr 20 '21 at 22:43