Why will script work with /bin/bash but not /bin/sh? - bash

I am trying to understand why the script will work with #!/bin/bash but not #!/bin/sh. I am running Cygwin and both sh.exe and bash.exe seem to be identical (same file size).
$ cat 1.sh
#!/bin/sh
while read line; do
echo ${line:0:9}
done < <(help | head -5)
$ ./1.sh
./1.sh: line 4: syntax error near unexpected token `<'
./1.sh: line 4: `done < <(help | head -5)'
$ cat 2.sh
#!/bin/bash
while read line; do
echo ${line:0:9}
done < <(help | head -5)
$ ./2.sh
GNU bash,
These she
Type `hel
Use `info
Use `man

Despite being same file, shell analyzes its own name when run and switches to either plain shell or bash mode.

The Problems
Bash is a superset of the Bourne shell, so many things are possible in Bash that aren't possible in more limited shells.
Even when sh is a hardlink to bash, it behaves differently when invokes as sh. Many features supported by the Bash shell will not work in this mode.
The Solutions
Remove your bashisms from the script, including nifty features like the process substitution you have on line 4.
Run your script as Bash, not vanilla Bourne.

Related

Bash bad substitution when trying to uppercase first letter

I am banging my head against the wall about the Bad substitution error in Bash. Consider the following code:
getApiName() {
IFS='-' # hyphen (-) is set as delimiter
read -ra array <<< "$1" # str is read into an array as tokens separated by IFS
for i in "${array[#]}"; do # access each element of array
output+=${i^} #set first letter to uppercase
done
IFS=' '
echo ${output}
}
When I do the following:
getApiName "vl-date-picker"
I get line 21: ${i^}: bad substitution
I have no clue on what's wrong.
Can you guys help me please?
Thanks in advance.
Regards
General Answer
I cannot reproduce your problem. I see two possible reasons:
You are using a non-bash shell.
Check this by adding the command ps to the script and look at the output. If there is no bash in the output, then you are running something different. A shebang #! /bin/bash at the beginning of your script helps to ensure that bash is used but is not a guarantee. ✱
You have an old version of bash which does not support ${i^}
(for instance that 15 (!) year old version pre-installed on Mac OS X).
You can check your bash version using bash --version. ${i^} was introduced in bash 4.0, as can be read here (search for hh. There are new case-modifying word expansions) or here.
Either way, you can use a different command which should work in all Posix shells.
If you have the GNU version of sed (check with sed --version) this command could be
getApiName() {
printf %s "$1" | sed -E 's/(^|-+)(.)/\U\2/g'
}
Nmp-Specific Answer
✱
The documentation of npm-run-script states
The actual shell your script is run within is platform dependent. By default, on Unix-like systems it is the /bin/sh command, on Windows it is the cmd.exe. The actual shell referred to by /bin/sh also depends on the system. As of npm#5.1.0 you can customize the shell with the script-shell configuration.
So to fix your problem you simply have to configure npm such that bash is used instead.
As a workaround, you could also call bash directly in your script. The simplest way to do so is a here-document:
bash -s -- "$#" <<"EOF"
# your original script here
EOF

awk and bash script? [duplicate]

This question already has answers here:
Syntax error in shell script with process substitution
(4 answers)
Closed 3 years ago.
I wonder why it doesn't work.
Please advise me.
1. working
$ nu=`awk '/^Mem/ {printf($2*0.7);}' <(free -m)`
$ echo $nu
1291.5
2. not working
$ cat test.sh
#!/bin/bash
nu=`awk '/^Mem/ {printf($2*0.7);}' <(free -m)`
echo $nu
$ sh test.sh
test.sh: command substitution: line 2: syntax error near unexpected token `('
test.sh: command substitution: line 2: `awk '/^Mem/ {printf($2*0.7);}' <(free -m)'
Could you please try following.
nu=$(free -m | awk '/^Mem/ {print $2*0.7}')
echo "$nu"
Things taken care are:
Use of backtick is depreciated so use $ to store variable's value.
Also first run free command pass its standard output as standard input to awk command by using |(which should be ideal way of sending output of a command to awk in this scenario specially) and save its output to a variable named nu.
Now finally print variable nu by echo.
Since <(...) process substitution is supported by bash not by sh so I am trying to give a solution where it could support without process substitution (which I mentioned a bit earlier too).
The <( ) construct ("process substitution") is not available in all shells, or even in bash when it's invoked with the name "sh". When you run the script with sh test.sh, that overrides the shebang (which specifies bash), so that feature is not available. You need to either run the script explicitly with bash, or (better) just run it as ./test.sh and let the shebang line do its job.
The reason to add a shebang in a script is to define an interpreter directive if the file has execution permission.
Then, you should invoke it by, for example
$ ./test.sh
once you have set the permission
$ chmod +x test.sh

Shell script error on slackware based Linux OS

I have a shell script that works on Ubuntu and provides me an output as I desire. When I test the same on a slackware linux version, my script fails.
The script fails at:
dialog --title "Test" --gauge "Copying file." 6 100 < <(
rsync -a --progress test.tar.gz /media/sda1 |
unbuffer -p grep -o "[0-9]*%" |
unbuffer -p cut -f1 -d '%'
)
The error is:
Syntax error near unexpected token `<'
What could be different between the two operating systems that the script fails to execute?
The script executes successfully if I get rid of the dialog command and the brackets etc.
Most likely, you are trying to run a bash script with non-bash shell. Or with older bash version.
First, try running it through bash explicitly, i.e.:
bash script.sh
You should also fix your shebang to point at bash:
#!/bin/bash
[Update below]
The < <( ... ) notation is unique for bash and zsh. The syntax error is a clear sign it is not recognised by the slackware shell.
Either slackware does not use bash, or its version of bash is too old for this feature.
Check the value of $BASH_VERSION on both platforms.
A possible alternative for
cat < <(
...
...
)
could be:
cat <<< "$(
...
...
)"
This will work in bash, ksh93, and zsh, and has been around slightly longer.
UPDATE
Based on your feedback, I've looked at the actual pipeline you try to use here.
I believe it's your intention to use column 3 of the --progress output as input for the dialog graphical progress indicator.
I tried this with a directory with lots of small files. Are you aware that this percentage indicator is per file? With my small files, rsync gave only one update per file. As every single file was written in one go, all percentages were equal to 100%.

"< <(command-here)" shell idiom resulting in "redirection unexpected"

This command works fine:
$ bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
However, I don't understand how exactly stable is passed as a parameter to the shell script that is downloaded by curl. That's the reason why I fail to achieve the same functionality from within my own shell script - it gives me ./foo.sh: 2: Syntax error: redirection unexpected:
$ cat foo.sh
#!/bin/sh
bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
So, the questions are: how exactly this stable param gets to the script, why are there two redirects in this command, and how do I change this command to make it work inside my script?
Regarding the "redirection unexpected" error:
That's not related to stable, it's related to your script using /bin/sh, not bash. The <() syntax is unavailable in POSIX shells, which includes bash when invoked as /bin/sh (in which case it turns off nonstandard functionality for compatibility reasons).
Make your shebang line #!/bin/bash.
Understanding the < <() idiom:
To be clear about what's going on -- <() is replaced with a filename which refers to the output of the command which it runs; on Linux, this is typically a /dev/fd/## type filename. Running < <(command), then, is taking that file and directing it to your stdin... which is pretty close the behavior of a pipe.
To understand why this idiom is useful, compare this:
read foo < <(echo "bar")
echo "$foo"
to this:
echo "bar" | read foo
echo "$foo"
The former works, because the read is executed by the same shell that later echoes the result. The latter does not, because the read is run in a subshell that was created just to set up the pipeline and then destroyed, so the variable is no longer present for the subsequent echo.
Understanding bash -s stable:
bash -s indicates that the script to run will come in on stdin. All arguments, then, are fed to the script in the $# array ($1, $2, etc), so stable becomes $1 when the script fed in on stdin is run.

Problem with temporary unnamed pipe in bash script

I have the following question:
When I execute the following script directly in a terminal window, the commands behave as expected.
$ diff <(echo tmp) <(echo tmp1)
1c1
< tmp
---
> tmp1
However when I write the same command in a shell script
#! /bin/bash
diff <(echo tmp) <(echo tmp1)
I get the following error message:
$ sh test.sh
test.sh: line 2: syntax error near unexpected token `('
test.sh: line 2: ` diff <(echo tmp) <(echo tmp1)'
Initially I thought this was an issue with diff, but this also happens with other commands. Does anybody have an idea what causes the problem?
Try
bash test.sh
or
chmod ugo+x test.sh
./test.sh
Works fine for me when I do either.
Looks like the syntax is not supported by the bourne shell (sh).
When bash is invoked using sh, it starts up in a special, POSIX-compliant mode. This has different syntax, which I guess explains the different results.
See bashref of POSIX mode, #22: "process substitution is not available".
That syntax doesn't look familiar. Are you sure you are using bash in your terminal? You can verify by typing echo $SHELL.

Resources