sudo command behaviour with quotes - linux

I need your help in understanding this behaviour of sudo.
sudo -s -- 'ls -l' this command works but sudo 'ls -l' throws error saying
sudo: ls -l: command not found I realize it treats the entire string within quote as single command (including the spaces) but what I don't get is how does it work fine with -s flag but fails when -s is not there.

Without -s, the first argument is the name of the command to execute. With -s, the first argument is a string passed to the -c option of whatever shell ($SHELL or your system shell) is used to execute the argument.
That is, assuming $SHELL is sh, the following are equivalent:
sudo -s -- 'ls -l'
sudo -- sh -c 'ls -l'

From the sudo man page:
-s [command]
The -s (shell) option runs the shell specified by the SHELL environment variable if it is set or the shell as specified in
the password database. If a command is specified, it is passed to the
shell for execution via the shell's -c option. If no command is
specified, an interactive shell is executed.
It behaves like it does because a new shell is spawned which breaks up the words in your "quoted command" like shells do.

Related

How can I feed input within bash [Executed through the Network]

As the title says, within linux how can I feed input to the bash when I do sudo bash
Lets say I have a bash script that reads the name.
The way I execute the script is through sudo using:
cat read-my-name-script.sh | sudo bash
Lets just say this is how I execute the script throught the network.
Now I want to fill the name automatically, is there a way to feed the input. I tried doing this: cat read-my-name-script.sh < name-input-file | sudo bash where the name-input-file is a file for the input that the user will be using to feed the script.
I am new to linux and learning to automate the input and wanted to create a file for input where the user can fill it and feed it to my script.
This is convoluted, but might do what you want.
sudo bash -c "$(cat read-my-name.sh)" <name-input-file
The -c says the next quoted argument are the commands to run (so, read the script as a string on the command line, instead of from a file), and the calling shell interpolates the contents of the file inside the double quotes before the sudo command gets evaluated. So if read-my-name.sh contains
#!/bin/bash
read -p "I want your name please"
then the command gets expanded into
sudo bash -c '#!/bin/bash
read -p "I want your name please"' <name-input-file
(where of course at this time the shell has actually removed the outer double quotes altogether; I put in single quotes in their place instead to show how this would look as actually executable, syntactically valid code).
I think you need that:
while read -r arg; do sudo bash read-my-name-script.sh "$arg";done <name-input-file
So each line of name-input-file will be passed as argument to sudo bash read-my-name-script.sh
If your argslist located on http server, you can do that:
while read -r arg; do sudo bash read-my-name-script.sh "$arg";done < <(wget -q -O- http://some/address/in/internet/name-input-file)
UPD
add [[ -f name-input-file ]] && readarray -t args <name-input-file
to read-my-name-script.sh
and use "${args[#]}" as arguments of command in the script.
For example echo "${args[#]}" or cmd "${args[0]}" "${args[1]}" ... "${args[100]}" in any order.
In this case you can use
wget -q -O- http://some/address/in/internet/read-my-name-script.sh | bash
for run your script with arguments from name-input-file whitout saving script to the local machine

How can I pass arguments to a script executed by sh read from stdin?

I download some shell script from example.com with wget and execute it immediately by streaming stdout of wget via a pipe to stdin of the sh command.
wget -O - http://example.com/myscript.sh | sh -
How can I pass arguments to the script?
You need to use -s option while invoking bash for passing an argument to the shell script being downloaded:
wget -O - http://example.com/myscript.sh | bash -s 'arg1' 'arg2'
As per 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.
While the accepted answer is correct, it does only work on bash and not on sh as the initial poster requested.
To do this in sh you'll have to add --:
curl https://example.com/script.sh | sh -s -- --my-arg

Option -l of exec shell command

Could you please clarify on the use of -l option of exec shell command. I didn't notice any difference when I ran exec ls | cat and exec -l ls | cat.
The -l option of exec adds a - at the beginning of the name of your command. For example:
exec -l diff | head
-diff: missing operand after '-diff'
-diff: Try '-diff --help' for more information.
Note the - everywhere before diff.
The point of all this? If you have a - before a command to start a shell it will act as a login shell. From man bash:
A login shell is one whose first character of argument zero is a -, or one started with the --login option.
Now, man exec states that:
If the -l option is supplied, the shell places a dash at the beginning of the zeroth argument passed to command. This is
what login(1) does.
So exec -l bash will run bash as a login shell. To test this, we can use the fact that a login bash executes the file ~/.bash_profile, so:
$ cat ~/.bash_profile
#!/bin/sh
printf "I am a login shell!\n"
If I start a login bash, the command printf "I am a login shell!\n" will be executed. Now to test with exec:
$ exec bash
$
Nothing is displayed, we are on a non-login shell.
$ exec -l bash
I am a login shell!
$
Here we have a login shell.

How to pgrep over ssh, or use pgrep as a larger bash command?

I'd like to run pgrep to find the ID of some process. It works great, except when run as a larger bash command as pgrep will also match it's parent shell/bash process which includes the match expression as part of it command-line.
pgrep sensibly excludes it own PID from the results, but less sensibly, doesn't seem to have an option to exclude its parent process(es).
Anyone come across this and have a good workaround.
Update.
pgrep -lf java || true
works fine, but
bash -c "(pgrep -lf java || true)"
echo 'bash -c "(pgrep -lf java || true)"' | ssh <host>
also identify the parent bash process.
I'm using pgrep as part of a much larger system, which it why the extra madness.
I ran into this issue using python's os.system(command) which executes the command in a subshell.
pgrep does not match itself, but it does match it's parent shell which includes pgrep's arguments.
I found a solution:
pgrep -f the-arguments-here[^\[]
The [^\[] regex assures that it does not match the [ (the beginning of the regex itself) and thus excludes the parent shell.
Example:
$ sh -c "pgrep -af the-arguments-here"
12345 actual-process with the-arguments-here
23456 sh -c pgrep -af the-arguments-here
vs:
$ sh -c "pgrep -af the-arguments-here[^\[]"
12345 actual-process with the-arguments-here
I'm still not seeing why you need the bash -c see part. You should be able to do
ssh <host> pgrep -lf java || true
which would actually run true on the local machine, but you could do
sssh <host> "pgrep -lf java || true"
if you needed true to be on the remote side. Again, assuming your shell accepts that syntax (i.e., is bash)
You're already running everything on the other side of the ssh in a bash shell, so I don't think you need to explicitly invoke bash again--unless your default shell is something else then you may want to consider either changing that or scripting in the appropriate default shell.

Error q : Command not found on Vim

I created a bash script :
#!/bin/bash
su root -c vim $1 -c ':%s/^M//g' -c 'wq'
My script has to remove all the ^M (carriage return on Windows) on my file, then save it.
When I execute my script it returns :
/sequenceFiles/Sequence1.seq: wq: command not found
Does someone know why ?
Thanks for your help.
The -c is seen by su, not vim, and the shell complains about the unknown command.
You need to pass the command as one argument to su:
su root -c "vim $1 -c ':%s/^M//g' -c 'wq'"
man su says:
`-c COMMAND'
`--command=COMMAND'
Pass COMMAND, a single command line to run, to the shell with a
`-c' option instead of starting an interactive shell.
Try
su root -c "vim $1 -c ':%s/^M//g' -c 'wq'"
While you can do it with vim, consider simpler:
perl -pi -e 's/\r\n/\n/' file

Resources