I have a script which runs fine when executed in Bash shell (with Red Hat Linux), however this same script which fails on a Solaris 10 (DB) server where ksh is being used to execute this script. This script basically reads line by line from a file and executes a stored proc (in Oracle). Below is my script :
#/bin/sh
for i in $(cat subscriber.txt); do
SUBSCRIBER_ID="'$i'"
sqlplus -s myuser/myuser <<EOF
execute delete_learnings($SUBSCRIBER_ID);
commit;
EXIT
EOF
done
The error I get is :
./removeLearnings.sh: syntax error at line 3: `$' unexpected
Any idea what might be going wrong? Should I change the script to have the ksh? I am not able to debug on this machine since it's a customer environment (which I don't have access to).
The issue is the $(...) construction which is POSIX compliant but unsupported by the legacy Bourne shell which /bin/sh is on Solaris 10 and older.
You can either replace your shebang to call the Solaris POSIX compliant shell:
#!/usr/xpg4/bin/sh
or use this legacy syntax (less recommended):
for i in `cat subscriber.txt`; do
you are trying to execute a sh( bourne shell) script on ksh (Korn shell). Try changing the shebang (#!/bin/bash) to (#!/bin/ksh)
Looping over text files with for is a bad idea, anyway. See http://mywiki.wooledge.org/BashFAQ/001- the recommended syntax is more portable, too:
while read stuff; do
: things with "$stuff"
done <subscriber.txt
You would normally use read -r but I don't know if that's available on Solaris.
However, very often, a shell loop is altogether the wrong approach. A single SQL invocation is a lot better and more robust:
( sed 's/.*/execute delete_learnings(&);/'
printf "commit;\nEXIT\n" ) |
sqlplus -s myuser/myuser
Related
So I was trying to create a script on bash shell, I came to know that the script doesn't run on ksh or dash shells. So my question is how you make a script to run on all 3 (bash, dash & ksh) shells.
In order to write a script that is guaranteed to be portable between the various shells, the script must be POSIX Shell compliant. POSIX is a minimum set of builtins and commands that all conforming shells must support. Ash, Dash, Zsh, Bash, Ksh, etc.. are all shells capable of running scripts that are POSIX compliant.
What shells like Bash do is add nice features which make the shell more capable, like additional parameter expansions for conversion to upper/lower case, substring replacement, etc.. and new builtins like [[ ... ]] that provide regex matching capabilities, etc.. While this makes Bash more capable, it also means scripts written using "Bashisms" are no longer able to run under all other shells. Ash, Dash and other minimal shells have no idea how to handle the features added by Bash, Ksh or Zsh and therefore fail.
To write truly portable scripts, you must limit the content to that provided by the POSIX command language.
You need something file like this:
#!/bin/bash #isn't a simple comment
echo "hello bash"
#!/bin/sh #isn't a simple comment
echo "hello sh"
#!/bin/ksh #isn't a simple comment
echo "hello ksh"
( #!) it's called shebang tells the shell what program to interpret the script
called this file as you better prefert (file.bsk), but don't forget give it execute permission it with :
chmod +x file.bsk
then run ./file.bsk
Some commands or utilities are not available in all shells or they might have different behavior in different shell. If you know which command run on which shell or gives you desired output you can write shell specific commends as below
bash -c 'echo bash'
ksh -c 'echo ksh'
All other commands that are common to all shell can be written in normal way.
My shell-script is failing on SUSE Linux as the stream-redirection operator I have used (&>>) is not working there, (But it is working fine in Other distributions). How can I correct this. Also I would like to know the standard way of doing the same which is supported by all Distributions?
The command you were using should mean you were using bourne shell:
ls &>> file
this command should redirect both stdout and stderr at the end of file.
Another way to write it, again with a bourne shell could be:
ls >> file 2>&1
However it seems to me this way to write the command will be recognized by more shells, I think for instance ksh will recognize the second form but not the first.
With csh or a csh-like shell you will need to use this syntax:
ls >>& file
Edit: I was confused because depending on the shell you can use &>> or >>& which are not the same.
I have some shell scripts which I run in my Linux/AIX machine with bash profile. Now my bash profile is going to be remove, and I will have Korn shell (ksh) or the C shell (csh). How to verify whether my scripts will run fine in Korn shell (ksh) or C shell (csh), even after bash shell is removed. Also, is there any differnce in commonly used commands between bash and other (ksh, csh). Is there command to check, which shell is getting used while running the shell script.
First of all, this is not a problem, the default shell of your account is irrelevant. As long as bash is installed on the machine, you can use it to run your code. Either add a shebang line as the first line of your script:
#!/usr/bin/env bash
Or, explicitly run the script with bash:
$ /bin/bash /path/to/script.sh
As for the differences, yes there are many. A script written for bash will not run in csh, their syntax is completely different. It might run on ksh but that will depend on your script. Not all features of the two shells are the same. For example:
$ cat test.sh
var="foo";
echo $var;
$ bash ./test.sh
foo
$ ksh ./test.sh
foo
$ csh ./test.sh
var=foo: Command not found.
var: Undefined variable.
As you can see above, var=foo runs correctly in ksh (which is part of the bourne shell family) but fails for csh. Basically, think of each shell as its own programming language. You wouldn't expect the python interpreter to be able to run a perl program, why do you expect one shell to be able to run a script written for another?
OP writes bash is going to be removed.
If you really cannot get bash installed. start each script with #!/bin/ksh and check for syntax problems:
ksh -n migrated_script
When you use bash/linux specific things you need to address them:
AIX will be "missing" flags on commands like find (changed last hour...) and ksh itself is also different.
Do not try csh, that is completely different.
I'm writing a shell script called myShellScript.sh inside of which I have the following text:
echo *** Print out this line ****
diff <(./myProgram) <(./otherProgram)
However, when I run sh myShellScript.sh I get the following error:
-bash-4.2$ sh myShellScript.sh
myShellScript.sh **** Print out this line **** myShellScript.sh
myShellScript.sh: line 2: syntax error near unexpected token `('
myShellScript.sh: line 2: `diff <(./myProgram) <(./otherProgram)'
Process substitution with the <(...) operator is a bash feature. You're getting that error message because your script is getting executed as something else (for example dash), or an older version of bash, or bash running in POSIX-compatibility mode with non-POSIX features like process substitution disabled (thanks, #chepner!)
If you want to execute the script with a full-featured bash, you have two options:
Run the script with bash:
bash myShellScript.sh
Set the first line of the script to #!/bin/bash (or whatever is the path to bash in your system), and run the script like this:
./myShellScript.sh
You need to execute your script with bash, not with sh.
You are using process substitution, which is not a standard POSIX shell feature. sh is a POSIX-compatible shell, so it does not support language extensions like process substitution. Bash will run with POSIX compatibility enabled if it is invoked as sh.
Therefore, you should execute scripts that require Bash-specific features using bash.
You clearly seem to be using bash, but for anyone reading this that needs to use a shell without support for process substitution, you can use the following:
# Instead of diff <(./myProgram) <(./otherProgram)
# A pair of named pipes to avoid using disk space
# for the output of myProgram and otherProgram
mkfifo myProgram.output
mkfifo otherProgram.output
./myProgram > myProgram.output &
./otherProgram > otherProgram.output &
diff myProgram.output otherProgram.output
rm myProgram.output otherProgram.output
This is nearly identical to how bash might perform process substitution on some platforms.
I added the following command near the top of my shell script in order to record the script output to a file. This works with no problem when I run the script as my user, jsmith, however when the script is run as root in a crontab, I receive an error:
syntax error near unexpected token:
exec &> >(tee $LOG_PATH$TIMESTAMP.log)
I do have both $LOG_PATH and $TIMESTAMP correctly defined above the command as:
LOG_PATH="/home/jsmith/script/logs/"
TIMESTAMP="$(date -d "today" +"%Y-%m-%d-%H:%M")"
Any ideas? Thanks!
Usually, Linux feature multiple shells (sh, csh, dash, bash, etc.) which have subtle syntax differences. It is possible that you tested your script with bash, whereas crontabs are executed with dash.
I suggest you the following:
check what shell your script requires (looks at the first line)
tell cron to use that shell, i.e., set SHELL=/bin/my_shell at the beginning of your crontab (see the manpage for details).