Run a bash file with parameters with Go - bash

I am trying to run a bash script that has parameters like so:
./test.sh param1 param2
bash file
param1=$1
param2=$2
echo $param1
echo $param2
However it does not work but it will work if the params are not there.
cmd, _ := exec.Command("/bin/sh", fmt.Sprintf("./test.sh %s %s","test1","test2")).Output()
But if I change the bash script to do something else without passing anything into it, then it works.
cmd, _ := exec.Command("/bin/sh", "./test.sh").Output()
How can I pass parameters into a bash file with Go?

sh expects the name of a script to run as its argument. You don't run sh './test.sh test1 test2' at a shell, you run sh ./test.sh test1 test2. The equivalent to that in Go is:
// KINDA BAD: Doesn't let the script choose its own interpreter
cmd, err := exec.Command("/bin/sh", "./test.sh", "test1", "test2")
If you were passing a shell script as an argument, that would be akin to the shell command sh -c './test.sh test1 test2' -- notice the -c argument. It's very bad practice (introduces serious security bugs), and you shouldn't ever do this, but if you were going to, it would look like:
// VERY BAD: Introduces serious security bugs if arguments are parameterized
cmd, err := exec.Command("/bin/sh", "-c", "./test.sh test1 test2")
But you shouldn't do any of that. Change your script to have a shebang:
#!/bin/sh
param1=$1
param2=$2
echo "$param1"
echo "$param2"
...save it as yourscript (no .sh!), set it to have executable permissions (chmod +x yourscript), and then run:
// GOOD: Lets your script choose its own interpreter
cmd, err := exec.Command("./yourscript", "test1", "test2")

Related

Bash pass argument to --init-file script

I'm running a shell script using bash --init-file script.sh that runs some commands, then leaves an interactive session open. How can I pass arguments to this init file from the process that runs the initial bash command? bash --init-file 'script.sh arg' doesn't work.
Interestingly, if the script contains echo "$# $*", passing an argument as I did above causes it to print nothing, while not passing an argument prints '0'.
Create a file with the content:
#!/bin/bash
script.sh arg
Pass that file to bash: bash --init-file thatfile
I'd like the arg to come from the command that runs bash with the
Create a file from the command line and pass it:
arg="$1"
cat >thatfile <<EOF
$(declare -p arg)
script.sh \"\$arg\"
EOF
bash --init-file thatfile
You might be interested in researching what is a process substitution in bash.

Redirect copy of stdin to file from within bash script itself

In reference to https://stackoverflow.com/a/11886837/1996022 (also shamelessly stole the title) where the question is how to capture the script's output I would like to know how I can additionally capture the scripts input. Mainly so scripts that also have user input produce complete logs.
I tried things like
exec 3< <(tee -ia foo.log <&3)
exec <&3 <(tee -ia foo.log <&3)
But nothing seems to work. I'm probably just missing something.
Maybe it'd be easier to use the script command? You could either have your users run the script with script directly, or do something kind of funky like this:
#!/bin/bash
main() {
read -r -p "Input string: "
echo "User input: $REPLY"
}
if [ "$1" = "--log" ]; then
# If the first argument is "--log", shift the arg
# out and run main
shift
main "$#"
else
# If run without log, re-run this script within a
# script command so all script I/O is logged
script -q -c "$0 --log $*" test.log
fi
Unfortunately, you can't pass a function to script -c which is why the double-call is necessary in this method.
If it's acceptable to have two scripts, you could also have a user-facing script that just calls the non-user-facing script with script:
script_for_users.sh
--------------------
#!/bin/sh
script -q -c "/path/to/real_script.sh" <log path>
real_script.sh
---------------
#!/bin/sh
<Normal business logic>
It's simpler:
#! /bin/bash
tee ~/log | your_script
The wonderful thing is your_script can be a function, command or a {} command block!

How to make the Makefile echos to use `/bin/echo -e`?

I know I can use this:
SHELL := /bin/bash
And make will use /bin/bash shell. Can I do the same with echo command? For example:
SHELL := /bin/bash
ECHO := /bin/echo -e
all:
#echo Fixed echo?
I always thought that using ECHOCMD := /bin/echo -e on the top of my Makefile would do this. But after searching, I could not find any reference supporting that ECHOCMD := /bin/echo -e has any special meaning to change the default #echo command.
Is the only alternative to use:
SHELL := /bin/bash
ECHO := /bin/echo -e
all:
#${ECHO} Fixed echo?

Run shell command with "redirecting of an output from a subshell" statement within go

According to run bash command in new shell and stay in new shell after this command executes, how can I run command:
bash --rcfile <(echo "export PS1='> ' && ls")
within golang? I've tried many combinations of exec.Command() but they did't work. For example:
exec.Command("bash", "--rcfile", `<("echo 'ls'")`)
I've also read this os, os/exec: using redirection symbol '<' '>' failed, but I think maybe my case is a bit more complicated.
You're almost there - I think the confusion is you're using piping to call bash, which means you actually need to call bash using bash:
exec.Command("bash", "-c", `bash --rcfile <(echo "export PS1='> ' && ls")`)

How to trap ERR when using 'set -e' in Bash

I have a simple script :
#!/bin/bash
set -e
trap "echo BOO!" ERR
function func(){
ls /root/
}
func
I would like to trap ERR if my script fails (as it will here b/c I do not have the permissions to look into /root). However, when using set -e it is not trapped. Without set -e ERR is trapped.
According to the bash man page, for set -e :
... A trap on ERR, if set, is executed before the shell exits. ...
Why isn't my trap executed? From the man page it seems like it should.
chepner's answer is the best solution: If you want to combine set -e (same as: set -o errexit) with an ERR trap, also use set -o errtrace (same as: set -E).
In short: use set -eE in lieu of just set -e:
#!/bin/bash
set -eE # same as: `set -o errexit -o errtrace`
trap 'echo BOO!' ERR
function func(){
ls /root/
}
# Thanks to -E / -o errtrace, this still triggers the trap,
# even though the failure occurs *inside the function*.
func
A more sophisticated example trap example that prints the message in red and also prints the exit code:
trap 'printf "\e[31m%s: %s\e[m\n" "BOO!" $?' ERR
man bash says about set -o errtrace / set -E:
If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.
What I believe is happening:
Without -e: The ls command fails inside your function, and, due to being the last command in the function, the function reports ls's nonzero exit code to the caller, your top-level script scope. In that scope, the ERR trap is in effect, and it is invoked (but note that execution will continue, unless you explicitly call exit from the trap).
With -e (but without -E): The ls command fails inside your function, and because set -e is in effect, Bash instantly exits, directly from the function scope - and since there is no ERR trap in effect there (because it wasn't inherited from the parent scope), your trap is not called.
While the man page is not incorrect, I agree that this behavior is not exactly obvious - you have to infer it.
You need to use set -o errtrace for the function to inherit the trap.
We have these options for debugging:
-e Exit immediately on failure
-E If set, any trap on ERR is inherited by shell functions
-u Exit when there is an unbound variable
-o Give a option-name to set
pipefail The return values of last (rightmost) command (exit code)
-v Print all shell input lines as they are read
-x Print trace of commands
For handling the errors we can catch directory with trap
trap 'echo >&2 "Error - exited with status $? at line $LINENO' ERR
Or a better version ref :
trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR
Or a function:
function __error_handing__(){
local last_status_code=$1;
local error_line_number=$2;
echo 1>&2 "Error - exited with status $last_status_code at line $error_line_number";
perl -slne 'if($.+5 >= $ln && $.-4 <= $ln){ $_="$. $_"; s/$ln/">" x length($ln)/eg; s/^\D+.*?$/\e[1;31m$&\e[0m/g; print}' -- -ln=$error_line_number $0
}
and call it this way:
trap '__error_handing__ $? $LINENO' ERR
Replace ERR with EXIT and it will work.
The syntax of the trap command is: trap [COMMANDS] [SIGNALS]
For more info, please read http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html

Resources