What's wrong with this bash script? - bash

Here's my myscript.sh:
alias apt-get-update="apt-get update -qq"
alias apt-get-install="apt-get install -f -y -qq --force-yes"
alias yum-install="yum --quiet --nogpgcheck -y install"
function ensure_cmd_or_install_package_apt(){
local cmd=$1
shift
local pkg=$*
hash $cmd 2>/dev/null || ( apt-get-update && apt-get-install $pkg )
}
When I run sh myscript.sh I get:
myscript.sh: 5: myscript.sh: Syntax error: "(" unexpected
It looks perfectly fine to me; any ideas?

Does running bash myscript.sh fix it?
It could be your script is running in dash instead of bash.
According to this answer you can change it with the following command:
chsh

Also in general you should be explicit in your script header as to the app/shell you want to run the script with:
#!/bin/bash
If the script requires bash-isms then you should tell it to run with bash.
In your case you are on ubuntu (confirm) and ubuntu uses dash as the default shell (ie /bin/sh is a symlink to dash). Dash doesn't allow:
function name () {}
and instead just wants:
name () {}
In fact the first form should be avoided if possible since it's not portable. But if you use the function keyword, don't use parens since they are not required (it's an accident that it even works).
With regards to setting a script header, sometimes it's better to use env to find the program (say in the case of ruby or perl for instance where you might have numerous ruby/perl executables but the one furthest up on your path is the one you want to run with).
#!/usr/bin/env ruby
Is the way to go. Usually for shells, /bin/bash or /bin/csh etc is sufficient in your shebang, but never assume :).

Related

Why this command "google-auth" works in the terminal but not from bash script?

I have installed libpam-google-authenticator and freeradius on server ubuntu 16.0405. Everything works good, except for if I use the command google-auth in bash script I get a error message "google-auth: command not found"
But the same works if I put it on terminal directly.
#!/bin/bash
google-auth
That is not a bash script.
To make it a bash script, your first line needs to include a "#" as follows:
#!/bin/bash
google-auth
Also, you need to ensure that the script is executable:
chmod +x yourscript.sh
Hopefully that will solve your problem.
As per the comments below, it seems like the command "google-auth" was an alias which wasn't being established in the child shell.

Sending Bash Aliases to detached screen sessions

I'm on a Linux machine using screen, and I'm attempting to write a (fairly portable) function which runs a bash function in a new, detached screen session which automatically closes upon completion. I've had some success, but I noticed the following behavior:
If I include the definition of mail_submit() in my ~/.bashrc file, I can run
mail_submit foo
in the terminal, and also I can access the alias in a new screen session:
screen -S test
mail_submit foo
However, the following command does not work:
screen -d -m -S test sh -c 'mail_submit foo'
presumably because sh -c starts a fresh shell that has no knowledge of my ~/.bashrc profile. So, I can use the following fix:
screen -d -m -S test sh -c 'source ~/.bashrc; mail_submit foo'
which does work.
But if I want to wrap this functionality up into a bash alias (which is my ultimate goal here), this will cause a weird self-referential situation.
Question: What is an easy way to either have sh -c know the location of my ~/.bashrc profile, or use a variant of sourcing the file and creating an alias?
EDIT: I could save the shell script in my home directory, and create an alias which runs
screen -d -m -S test bash -c '~/mail_submit.sh $1'
but I'd still be curious to hear other possible fixes.
A default ~/.bashrc contains this ([[ "$-" != *i* ]] && return) little piece of code on top of it (or somewhere else in the upper part). This line will prevent the ~/.bashrc from beeing sourced if the bash shell doesn't run in interactive mode.
You could:
Remove this line
Create a new file which will only contain the alias you need and source that
Create a little bash script instead of an alias and run that
Do you mean screen -d -m -S test bash -c 'mail_submit foo'?
It looks like you're trying to run the command with the shell (sh), and not the bourne again shell (bash), which is the shell interpreter which actually reads the ~/.bashrc profile.
Edit: The .bashrc file is not being sourced by default because screen does not create the bash process as a login shell, which is when the .bashrc file is read. Creating a .screenrc file with the line defshell -bash will create the bash process as a login shell instead, which will then call the .bashrc file.

ssh and chroot followed by cd in shell

How to excute cd command after chroot to a remote node in shell script?
For ex:
I need this.
ssh remote-node "chroot-path cd command here; extra commands"
Without chroot it works fine, If I put the command list in another shell script and execute the shell script after chroot it seems to run okay.
But chroot seems to break cd?
Use printf %q to have your local shell (which must be bash) give you correct quoting that works, and bash -c to explicitly invoke a remote shell compatible with that quoting (as %q can generate bash-only quoting with input strings that contain special characters) under your chroot.
cmd_str='cd /to/place; extra commands'
remote_command=( bash -c "$cmd_str" )
printf -v remote_command_str '%q ' "${remote_command[#]}"
ssh remote-node "chroot /path/here $remote_command_str"
The bash -c is necessary because cd is a shell construct, and chroot directly exec's its arguments (with no shell) by default.
The printf %q and correct (single-quote) quoting for cmd_str ensures that the command string is executed by the final shell (the bash -c invoked under the chroot), not your local shell, and not by the remote pre-chroot shell.
Assuming by chroot-path you mean chroot /some/root/path.
chroot only takes a single command and cd isn't a command it is a shell built-in so that won't work.
Additionally only cd command here is being run (or attempted to) under the chroot setup. Everything after the ; is running in the main shell.
A script is the easiest way to do what you want.

Shell script using with tmux fails on zsh

I have following script:
#!/usr/bin/env bash
# set -xv
tmux new-window -n 'foo' 'source "$HOME/.rvm/scripts/rvm"; sleep 123' \;
On one machine it works perfectly, on the second I got an error:
sh: 1: source: not found
Ofcourse running command from shell works perfectly.
What is wrong? Machines have similar dot files....
source is not a POSIX command. Use . instead. The machine that fails is probably using dash as the system shell, not bash. The fact that tmux is executed from a bash script does not mean bash is used to execute the command given to new-window. tmux will use the system shell /bin/sh, so the command should not rely on non-POSIX features like the source synonym for ..

sh: ...: is not an identifier when trying to invoke shell scripts using plink

Below is my shell script that I am trying to execute using PLINK on MachineB from MachineA(Windows Machine).
#!/bin/bash
export HIVE_OPTS="$HIVE_OPTS -hiveconf mapred.job.queue.name=hdmi-technology"
hive -S -e 'SELECT count(*) from testingtable1' > attachment22.txt
I am using plink to execute the shell script like below,
C:\PLINK>plink uname#MachineB -m test.sh
Using keyboard-interactive authentication.
Password:
Using keyboard-interactive authentication.
Your Kerberos password will expire in 73 days.
And this is the below error I always get whenever I try to run like above.
sh: HIVE_OPTS= -hiveconf mapred.job.queue.name=hdmi-technology: is not
an identifier
Something wrong with my shell script? or some trailing spaces? I am not able to figure it out. I am running PLINK from windows machine
The sh: prefix on the error message indicates that the script is being executed by sh, not bash.
bash lets you combine setting a variable and exporting it into a single command:
export foo=bar
sh, or at least some older versions of it, require these two actions to be separated:
foo=bar ; export foo
A version of sh that doesn't recognize the export foo=bar syntax will interpret the string foo=bar as a variable name (and an illegal one, since it isn't an identifier).
Either arrange for the script to be executed by bash, or change this:
export HIVE_OPTS="$HIVE_OPTS -hiveconf mapred.job.queue.name=hdmi-technology"
to this:
HIVE_OPTS="$HIVE_OPTS -hiveconf mapred.job.queue.name=hdmi-technology"
export HIVE_OPTS
For that matter, since you're referring to $HIVE_OPTS at the very beginning of your script, it's almost certainly already exported, so you could just drop the export.
(You'll also need to avoid any other bash-specific features.)
So why is the system invoking the shell with sh? The #!/bin/bash syntax is specific to Unix-like systems. Windows generally decides how to execute a script based on the file extension; apparently your system is configured to invoke *.sh files using sh. (You could configure your system, using Folder Options, to invoke *.sh files using bash, but that might introduce other problems.)
I think the -m option to plink is for reading commands to execute on the remote machine from a local file. If my comment about line endings doesn't work, try
plink uname#MachineB test.sh
Make sure test.sh is executable by running
chmod +x test.sh
on MachineB.

Resources