What does bash -s do? - bash

I'm new to bash and trying to understand what the script below is doing, i know -e is exit but i'm not sure what -se or what the $delimiter is for?
$delimiter = 'EOF-MY-APP';
$process = new SSH(
"ssh $target 'bash -se' << \\$delimiter".PHP_EOL
.'set -e'.PHP_EOL
.$command.PHP_EOL
.$delimiter
);

The -s options is usually used along with the curl $script_url | bash pattern. For example,
curl -L https://chef.io/chef/install.sh | sudo bash -s -- -P chefdk
-s makes bash read commands (the "install.sh" code as downloaded by "curl") from stdin, and accept positional parameters nonetheless.
-- lets bash treat everything which follows as positional parameters instead of options.
bash will set the variables $1 and $2 of the "install.sh" code to -P and to chefdk, respectively.
Reference: https://www.experts-exchange.com/questions/28671064/what-is-the-role-of-bash-s.html

From man bash:
-s If the -s option is present, or if no arguments remain after
option processing, then commands are read from the standard
input. This option allows the positional parameters to be
set when invoking an interactive shell.
From help set:
-e Exit immediately if a command exits with a non-zero status.
So, this tells bash to read the script to execute from Standard Input, and to exit immediately if any command in the script (from stdin) fails.
The delimiter is used to mark the start and end of the script. This is called a Here Document or a heredoc.

Related

Can't run "compgen -c" from perl script

I want to check if a command exists on my machine (RedHat) inside a perl script.
Im trying to check if compgen -c contains the desired command, but running it from inside a script just gives me an empty output. Other commands work fine.
example.pl:
my $x = `compgen -c`;
print $x;
# empty output
my $y = `ls -a`;
print $y;
# .
# ..
# example.pl
Are there possible solutions for this? Or is there a better way to check for commands on my machine?
First, Perl runs external commands using /bin/sh, which is nowadays a link to a shell that is a default-of-sorts on your system. Much of the time that is bash, but not always; on RedHat it is.
This compgen is a bash builtin. One way to discover that is to run man compgen (in bash) -- and the bash manual pops up. Another way is type as Dave shows.
To use builtins we generally need to run an explicit shell for them, and they have a varied behavior in regards to whether the shell is "interactive" or not.† I can't find a discussion of that in bash documentation for this builtin but experimentation reveals that you need
my #completions = qx(bash -c "compgen -c")
The quotes are needed so to pass a complete command to a shell that will be started.
Note that this way you don't catch any STDERR out of those commands. That will come out on the terminal, and it can get missed that way. Or, you can redirect that stream in the command, by adding 2>&1 (redirect to STDOUT) at the end of it.
This is one of the reasons to use one of a number of good libraries for running and managing external commands instead of the builtin "backticks" (the qx I use above is an operator form of it.)
† This can be facilitated with -i
my #output_lines = qx(bash -i -c "command with arguments")
It's because compgen is a bash built-in command, not an external command. And when you run a command using backticks, you get your system's default shell - which is probably going to be /bin/sh, not bash.
The solution is to explicitly run bash, using the -c command-line option to give it a command to run.
my $x = `bash -c compgen -c`;
From a bash prompt, you can use type to see how a command is implemented.
$ type ssh
ssh is /usr/bin/ssh
$ type compgen
compgen is a shell builtin

Allow user input in second command in bash pipe

I'm looking for how I might allow user input in a second command in a bash statement and I'm not sure how to go about it. I'd like to be able to provide a one-liner for someone to be able to install my application, but part of that application process requires asking some questions.
The current script setup looks like:
curl <url/to/bootstrap.sh> | bash
and then boostrap.sh does:
if [ $UID -ne 0 ]; then
echo "This script requires root to run. Restarting the script under root."
exec sudo $0 "$#"
exit $?
fi
git clone <url_to_repo> /usr/local/repo/
bash /usr/local/repo/.setup/install_system.sh
which in turn calls a python3 script that asks for input.
I know that the the curl in the first line is using stdin and so that might make what I'm asking impossible and that it has to be two lines to ever work:
wget <url/to/boostrap.sh>
bash bootstrap.sh
You can restructure your script to run this way:
bash -c "$(curl -s http://0.0.0.0//test.bash 2>/dev/null)"
foo
wololo:a
a
My test.bash is really just
#!/bin/bash
echo foo
python -c 'x = raw_input("wololo:");print(x)'`
To demonstrate that stdin can be read from in this way. Sure it creates a subshell to take care of curl but it allows you to keep reading from stdin as well.

How can i find the process name by the shell script that invokes it?

Is there a way, I can find the process name of bash script by the shell script that was used to invoke it? or can I set process name of bash script to something such as
-myprocess
(I have looked into argv[0], but I am not clear about it)
so when I use
ps -ef | grep -c '[M]yprocess'
I get only all the instances of myprocess?
To obtain the command name of the current script that is running, use:
ps -q $$ -o comm=
To get process information on all running scripts that have the same name as the current script, use:
ps -C "$(ps -q $$ -o comm=)"
To find just the process IDs of all scripts currently being run that have the same name as the current script, use:
pgrep "$(ps -q $$ -o comm=)"
How it works
$$ is the process ID of the script that is being run.
The option -q $$ tells ps to report on only process ID $$.
The option -o comm= tells ps to omit headers and to skip the usual output and instead print just the command name.
The parent process id can be obtained from $PPID on bash and ksh. We can read the fields from ps into an array.
For bash you can try this. The problem with ps is that many options are non-standard, so I have kept that as generic as possible:
#!/bin/bash
while read -a fields
do
if [[ ${fields[0]} == $PPID ]]
then
echo "Shell: ${fields[3]}"
echo "Command: ${fields[4]}"
fi
done < <(ps -p $PPID)
You have tagged bash and ksh, but they have different syntax rules. To read into an array bash uses -a but ksh uses -A, So for korn shell you would need to change the read line (and the #! line):
while read -A fields
Not sure what you mean by that, but let's go with an example script b.sh.
#!/usr/local/bin/bash
echo "My name is $0, and I am running under $SHELL as the shell."
Running the script will give you:
$ bash b.sh
My name is b.sh, and I am running under /usr/local/bin/bash as the shell.
For more check this answer: HOWTO: Detect bash from shell script

Trouble escaping quotes in a variable held string during a Sub-shell execution call [duplicate]

This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 6 years ago.
I'm trying to write a database call from within a bash script and I'm having problems with a sub-shell stripping my quotes away.
This is the bones of what I am doing.
#---------------------------------------------
#! /bin/bash
export COMMAND='psql ${DB_NAME} -F , -t --no-align -c "${SQL}" -o ${EXPORT_FILE} 2>&1'
PSQL_RETURN=`${COMMAND}`
#---------------------------------------------
If I use an 'echo' to print out the ${COMMAND} variable the output looks fine:
echo ${COMMAND}
screen output:-
#---------------
psql drupal7 -F , -t --no-align -c "SELECT DISTINCT hostname FROM accesslog;" -o /DRUPAL/INTERFACES/EXPORTS/ip_list.dat 2>&1
#---------------
Also if I cut and paste this screen output it executes just fine.
However, when I try to execute the command as a variable within a sub-shell call, it gives an error message.
The error is from the psql client to the effect that the quotes have been removed from around the ${SQL} string.
The error suggests psql is trying to interpret the terms in the sql string as parameters.
So it seems the string and quotes are composed correctly but the quotes around the ${SQL} variable/string are being interpreted by the sub-shell during the execution call from the main script.
I've tried to escape them using various methods: \", \\", \\\", "", \"" '"', \'"\', ... ...
As you can see from my 'try it all' approach I am no expert and it's driving me mad.
Any help would be greatly appreciated.
Charlie101
Instead of storing command in a string var better to use BASH array here:
cmd=(psql ${DB_NAME} -F , -t --no-align -c "${SQL}" -o "${EXPORT_FILE}")
PSQL_RETURN=$( "${cmd[#]}" 2>&1 )
Rather than evaluating the contents of a string, why not use a function?
call_psql() {
# optional, if variables are already defined in global scope
DB_NAME="$1"
SQL="$2"
EXPORT_FILE="$3"
psql "$DB_NAME" -F , -t --no-align -c "$SQL" -o "$EXPORT_FILE" 2>&1
}
then you can just call your function like:
PSQL_RETURN=$(call_psql "$DB_NAME" "$SQL" "$EXPORT_FILE")
It's entirely up to you how elaborate you make the function. You might like to check for the correct number of arguments (using something like (( $# == 3 ))) before calling the psql command.
Alternatively, perhaps you'd prefer just to make it as short as possible:
call_psql() { psql "$1" -F , -t --no-align -c "$2" -o "$3" 2>&1; }
In order to capture the command that is being executed for debugging purposes, you can use set -x in your script. This will the contents of the function including the expanded variables when the function (or any other command) is called. You can switch this behaviour off using set +x, or if you want it on for the whole duration of the script you can change the shebang to #!/bin/bash -x. This saves you explicitly echoing throughout your script to find out what commands are being run; you can just turn on set -x for a section.
A very simple example script using the shebang method:
#!/bin/bash -x
ec() {
echo "$1"
}
var=$(ec 2)
Running this script, either directly after making it executable or calling it with bash -x, gives:
++ ec 2
++ echo 2
+ var=2
Removing the -x from the shebang or the invocation results in the script running silently.

Pass args for script when going thru pipe

I have this example shell script:
echo /$1/
So I may call
$ . ./script 5
# output: /5/
I want to pipe the script into sh(ell), but can I pass the arg too ?
cat script | sh
# output: //
You can pass arguments to the shell using the -s option:
cat script | bash -s 5
Use bash -s -- <args>
e.g, install google cloud sdk
~ curl https://sdk.cloud.google.com | bash -s -- --disable-prompts
cat script | sh -s -- 5
The -s argument tells sh to take commands from standard input and not to require a filename as a positional argument. (Otherwise, without -s, the next non-flag argument would be treated as a filename.)
The -- tells sh to stop processing further arguments so that they are only picked up by the script (rather than applying to sh itself). This is useful in situations where you need to pass a flag to your script that begins with - or -- (e.g.: --dry-run) that must be ignored by sh. Also note that a single - is equivalent to --.
cat script | sh -s - --dry-run

Resources