What kind of command is "sudo", "su", or "torify" - linux

I know what they do. I was just wondering what kind of command are they. How can you make one using shell scripting.
For example, command like:
ignoreError ls /Home/
ignoreError mkdir /Home/
ignoreError cat
ignoreError randomcommand
Hope you get the idea

The way to do it in a shell script is with the "$#" construct.
"$#" expands to a quoted list of all of the arguments you passed to your shell script. $1 would be the command you want your shell script to run, and $2 $3 etc are the arguments to that command.
The only example I have is from cygwin. Cygwin does not have sudo, but I have this script that emulates it:
#!/usr/bin/bash
cygstart --action=runas "$#"
So when I run a command like
$ sudo ls -l
my sudo script does whatever it needs to do (cygstart --action=runas) and calls the ls command with the -l argument.

Try this script:
#!/bin/sh
"$#"
Call it, for example, run, make it runnable chmod u+x run, and try it:
$ run ls -l #or ./run ls -l
...
output of ls
...
The idea is that the script takes the parameters specified on the command line and use them as a (sub)command... Modify the script this way:
#!/bin/sh
echo "Trying to run $*"
"$#"
and you will see.

Related

Executing `sh -c` in a bash script

I have a test.sh file which takes as a parameter a bash command, it does some logic, i.e. setting and checking some env vars, and then executes that input command.
#!/bin/bash
#Some other logic here
echo "Run command: $#"
eval "$#"
When I run it, here's the output
% ./test.sh echo "ok"
Run command: echo ok
ok
But the issue is, when I pass something like sh -c 'echo "ok"', I don't get the output.
% ./test.sh sh -c 'echo "ok"'
Run command: sh -c echo "ok"
%
So I tried changing eval with exec, tried to execute $# directly (without eval or exec), even tried to execute it and save the output to a variable, still no use.
Is there any way to run the passed command in this format and get the ourput?
Use case:
The script is used as an entrypoint for the docker container, it receives the parameters from docker CMD and executes those to run the container.
As a quickfix I can remove the sh -c and pass the command without it, but I want to make the script reusable and not to change the commands.
TL;DR:
This is a typical use case (perform some business logic in a Docker entrypoint script before running a compound command, given at command line) and the recommended last line of the script is:
exec "$#"
Details
To further explain this line, some remarks and hyperlinks:
As per the Bash user manual, exec is a POSIX shell builtin that replaces the shell [with the command supplied] without creating a new process.
As a result, using exec like this in a Docker entrypoint context is important because it ensures that the CMD program that is executed will still have PID 1 and can directly handle signals, including that of docker stop (see also that other SO answer: Speed up docker-compose shutdown).
The double quotes ("$#") are also important to avoid word splitting (namely, ensure that each positional argument is passed as is, even if it contains spaces). See e.g.:
#!/usr/bin/env bash
printargs () { for arg; do echo "$arg"; done; }
test0 () {
echo "test0:"
printargs $#
}
test1 () {
echo "test1:"
printargs "$#"
}
test0 /bin/sh -c 'echo "ok"'
echo
test1 /bin/sh -c 'echo "ok"'
test0:
/bin/sh
-c
echo
"ok"
test1:
/bin/sh
-c
echo "ok"
Finally eval is a powerful bash builtin that is (1) unneeded for your use case, (2) and actually not advised to use in general, in particular for security reasons. E.g., if the string argument of eval relies on some user-provided input… For details on this issue, see e.g. https://mywiki.wooledge.org/BashFAQ/048 (which recaps the few situations where one would like to use this builtin, typically, the command eval "$(ssh-agent -s)").

Unix shell scripting: pass shell options (-x etc.) to nested scripts

How can I run nested shell scripts with the same option? For example,
parent.sh
#!/bin/sh
./child.sh
child.sh
#!/bin/sh
ls
How can I modify parent.sh so that when I run it with sh -x parent.sh, the -x option is effective in child.sh as well and the execution of ls is displayed on my console?
I'm looking for a portable solution which is effective for rare situations such as system users with /bin/false as their registered shell. Will the $SHELL environment variable be of any help?
Clarification: I sometimes want to call parent.sh with -x, sometimes with -e, depending on the situation. So the solution must not involve hard-coding the flags.
If you use bash, i can recommend the following:
#!/bin/bash
export SHELLOPTS
./child.sh
You can propagate as many times as you need, also you can use echo $SHELLOPTS in every script down the line to see what is happening and how options are propagated if you need to understand it better.
But for /bin/sh it will fail with /bin/sh: SHELLOPTS: readonly variable because of how POSIX is enforced on /bin/sh in various systems, more info here: https://lists.gnu.org/archive/html/bug-bash/2011-10/msg00052.html
it's looks like a hack and seems it's not the best way.
But it will do exact what you want
One of the ways how you can do it - it's to create aliases to create wrappers for sh:
alias saveShell='cp /bin/sh $some_safe_place'
alias shx='cp $some_safe_place /bin/x_sh; rm /bin/sh; echo "/bin/x_sh -x $#" > /bin/sh; chmod 755 /bin/sh '
alias she='cp $some_safe_place /bin/e_sh; rm /bin/sh; echo "/bin/e_sh -e $#" > /bin/sh; chmod 755 /bin/sh '
alias restoreShell='cp $some_safe_place /bin/sh'
How to Use:
run saveShell and then use shx or she , if you would change -x on -e run restoreShell and then run shx or she
run script as usually
sh ./parent.sh
BE VERY CAREFUL WITH MOVING SH
Other solution
replace #!/bin/sh to #!/bin/sh -x or #!/bin/sh -e with sed in all sh files before running script.

Piping a shell script to bash and launch interactive bash

Consider the following shell script on example.com
#/bin/bash
export HELLO_SCOPE=WORLD
eval $#
Now, I would like to download and then execute this shell script with parameters in the simplest way and be able to launch an interactive bash terminal with the HELLO_SCOPE variable set.
I have tried
curl http://example.com/hello_scope.sh | bash -s bash -i
But it quits the shell immediately. From what I can understand, it's because curls stdout, the script, remains the stdin of the bash, preventing it from starting interactively (as that would require my keyboard to be stdin).
Is there a way to avoid this without going through the extra step of creating a temporary file with the shell script?
You can source it:
# open a shell
. <(curl http://example.com/hello_scope.sh)
# type commands ...
You could just download this script you (using wget for example) and source this script, isn't it ?
script_name="hello_scope.sh"
[[ -f $script_name ]] && rm -rf "$script_name"
wget "http://example.com/$script_name" -O "$script_name" -o /dev/null
&& chmod u+x "$script_name"
&& source "$script_name"
You could use . "$script_name" instead of source "$script_name" if you want (. is POSIX compliant). You could write the previous code in a script and source it to have interactive shell with the setted variable $HELLO_SCOPE.
Finally you could remove the eval line in your remote shell script.

Run text file as commands in Bash

If I have a text file with a separate command on each line how would I make terminal run each line as a command? I just don't want to have to copy and paste 1 line at a time. It doesn't HAVE to be a text file... It can be any kind of file that will work.
example.txt:
sudo command 1
sudo command 2
sudo command 3
you can make a shell script with those commands, and then chmod +x <scriptname.sh>, and then just run it by
./scriptname.sh
Its very simple to write a bash script
Mockup sh file:
#!/bin/sh
sudo command1
sudo command2
.
.
.
sudo commandn
you can also just run it with a shell, for example:
bash example.txt
sh example.txt
Execute
. example.txt
That does exactly what you ask for, without setting an executable flag on the file or running an extra bash instance.
For a detailed explanation see e.g. https://unix.stackexchange.com/questions/43882/what-is-the-difference-between-sourcing-or-source-and-executing-a-file-i
You can use something like this:
for i in `cat foo.txt`
do
sudo $i
done
Though if the commands have arguments (i.e. there is whitespace in the lines) you may have to monkey around with that a bit to protect the whitepace so that the whole string is seen by sudo as a command. But it gives you an idea on how to start.
cat /path/* | bash
OR
cat commands.txt | bash

Redirecting the output of program which itself is an argument

Let me present the scenario first with the command which is not working under linux bash environment.
$ timed-run prog1 1>/dev/null 2>out.tmp
Here in the above case I want to redirect the output of program 'prog1' to /dev/null and out.tmp file. But this command is redirecting the output (if any) of timed-run to out.tmp.
Any help will be appreciated.
From a simple example, I experience exactly the opposite.
$ time ls 1> foo 2> bar
real 0m0.002s
user 0m0.004s
sys 0m0.000s
$ more foo
<show files>
$ more bar
<empty>
$
The output of ls is redirected, and the output of time is not!
The problem here is in timed-run not in bash. If you run the same command replacing timed-run with the standard time command this works as you expect. Mainly timed run needs to run the arguments of prog1 through the shell again. If it is a shell script you can do this with the eval command. For example:
#!/bin/sh
echo here is some output
echo $*
eval $*
now run
timed-run prog1 '1>/dev/null' '2>output.tmp'
How about using sh -c 'cmd' like so:
time -p sh -c 'ls -l xcvb 1>/dev/null 2>out.tmp'
time -p sh -c 'exec 0</dev/null 1>/dev/null 2>out.tmp; ls -l xcvb'
# in out.tmp:
# ls: xcvb: No such file or directory

Resources