"echo password | sudo -S" with exclamation mark - linux

What I want to do is
$ echo password!p | sudo -S [command]
in a linux terminal (in my case, ubuntu20.04). If you type it as it is, the exclamation mark will refer to the history of the command line, and if !p=pwd, it will be a string like passwordpwd and will not send the password!p correctly. I have tried every way I could find to escape the exclamation, like 'password!p', 'password!p', '\''password!p'\'', etc. The last one behaves the same as 'echo |' but did not result in using the command. Is there any way to get through this?

Could try turning off history expansion.
set +o histexpand
echo -n "password!p" | sudo -S command
set -o histexpand # if you want to restore the capability after

Related

The script sometimes doesn't run after wget

The script sometimes doesn't run after wget. Perhaps it is necessary to wait for the completion of wget?
#!/usr/bin/env bash
set -Eeuo pipefail
# Installing tor-browser
echo -en "\033[1;33m Installing tor-browser... \033[0m \n"
URL='https://tor.eff.org/download/' # Official mirror https://www.torproject.org/download/, may be blocked
LINK=$(wget -qO- $URL | grep -oP -m 1 'href="\K/dist.+?ALL.tar.xz')
URL='https://tor.eff.org'${LINK}
curl --location $URL | tar xJ --extract --verbose --preserve-permissions
sudo mv tor-browser /opt
sudo chown -R $USER /opt/tor-browser
cd /opt/tor-browser
./start-tor-browser.desktop --register-app
There are pitfalls associated with set -e (aka set -o errexit). See BashFAQ/105 (Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?).
If you decide to use set -e despite the problems then it's a very good idea to set up an ERR trap to show what has happened, and use set -E (aka set -o errtrace) so it fires in functions and subshells etc. A basic ERR trap can be set up with
trap 'echo "ERROR: ERR trap: line $LINENO" >&2' ERR
This will prevent the classic set -e problem: the program stops suddenly, at an unknown place, and for no obvious reason.
Under set -e, the script stops on any error.
set -Eeuo pipefail
# ^
Maybe the site is sometimes unavailable, or the fetched page doesn't match the expression grep is searching for.
You are doing
wget -qO- $URL
according to wget man page
-q
--quiet
Turn off Wget's output.
this is counterproductive for finding objective cause of malfunction, by default wget is verbose and write information to stderr, if you wish to store that into file you might redirect stderr to some file, consider following simple example
wget -O - http://www.example.com 2>>wget_out.txt
it does download Example Domain and write its' content to standard output (-) whilst stderr is appended to file named wget_out.txt, therefore if you run that command e.g. 3 times you will have information from 3 runs in wget_out.txt

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 automatically respond to a password prompt via the command line?

I'm looking to respond to a password prompt in the linux terminal. I know how to do this with echo and a non-password prompt. For example, let's say whatsyourname.sh prompted me for a string with my name after being ran, but didn't allow my name to be passed as an argument in the inital command. I would do the following:
echo -e "dan" | ./whatsyourname.sh
However, if I ran a command that asked me for a password after being ran, the following does not work:
echo -e "supersecurepassword" | sudo apt-get update
I'm guessing this has something to do with the fact that the characters are hidden while a password is being input in the command line. How would I respond to a password prompt within the inital command?
You're looking for sudo -S
Explaining -S - man sudo
-S, --stdin
Write the prompt to the standard error and read the password from the standard input instead of
using the terminal device. The password must be followed by a newline character.
Simple,
#!/bin/bash
echo "notsecure" | sudo -S apt-get update
Variable,
#!/bin/bash
pass="notsecure"
echo $pass | sudo -S apt-get update
Lets still type it,
#!/bin/bash
read -s -p "[sudo] sudo password for $(whoami): " pass
echo $pass | sudo -S apt-get update
Explaining -s and -p - help read
-r do not allow backslashes to escape any characters
-s do not echo input coming from a terminal
Handy if you make a script that logs into multiple servers to view route -n for example.

BASH automatically adding quotes to string

I'm trying to write a simple bash script that executes a command with one string variable. Upon execution bash adds single quotes to the string variable making the command useless. How do I execute the command without the quotes from the bash script?
#!/bin/bash
key=$(echo $1 | tr '[:lower:]' '[:upper:]')
sudo tee /proc/acpi/bbswitch \<\<\<$key
the output I get is
~/scripts$ bash -x nvidia on
++ echo on
++ tr '[:lower:]' '[:upper:]'
+ key=ON
+ sudo tee /proc/acpi/bbswitch '<<<ON'
the two commands I want to run without the quotes are either
sudo tee /proc/acpi/bbswitch <<<ON
or
sudo tee /proc/acpi/bbswitch <<<OFF
The problem isn't the quotes, it's that sudo doesn't execute the command via the shell. So metacharacters like <<< don't have any special meaning when they're given as sudo arguments. You need to invoke the shell explicitly:
sudo bash -c "tee /proc/acpi/bbswitch <<<$key"
But there doesn't really seem to be a need to use a here-string for this. Just use:
echo "$key" | sudo tee /proc/acpi/bbswitch
There's no need to quote the <<< operator. sudo doesn't read from its standard input by default; it passes it through to the command it runs.
sudo tee /proc/acpi/bbswitch <<< $key

"stdin: is not a tty" from cronjob

I'm getting the following mail every time I execute a specific cronjob. The called script runs fine when I'm calling it directly and even from cron. So the message I get is not an actual error, since the script does exactly what it is supposed to do.
Here is the cron.d entry:
* * * * * root /bin/bash -l -c "/opt/get.sh > /tmp/file"
and the get.sh script itself:
#!/bin/sh
#group and url
groups="foo"
url="https://somehost.test/get.php?groups=${groups}"
# encryption
pass='bar'
method='aes-256-xts'
pass=$(echo -n $pass | xxd -ps | sed 's/[[:xdigit:]]\{2\}/&/g')
encrypted=$(wget -qO- ${url})
decoded=$(echo -n $encrypted | awk -F '#' '{print $1}')
iv=$(echo $encrypted | awk -F '#' '{print $2}' |base64 --decode | xxd -ps | sed 's/[[:xdigit:]]\{2\}/&/g')
# base64 decode input and save to file
output=$(echo -n $decoded | base64 --decode | openssl enc -${method} -d -nosalt -nopad -K ${pass} -iv ${iv})
if [ ! -z "${output}" ]; then
echo "${output}"
else
echo "Error while getting information"
fi
When I'm not using the bash -l syntax the script hangs during the wget process. So my guess would be that it has something to do with wget and putting the output to stdout. But I have no idea how to fix it.
You actually have two questions here.
Why it prints stdin: is not a tty?
This warning message is printed by bash -l. The -l (--login) options asks bash to start the login shell, e.g. the one which is usually started when you enter your password. In this case bash expects its stdin to be a real terminal (e.g. the isatty(0) call should return 1), and it's not true if it is run by cron—hence this warning.
Another easy way to reproduce this warning, and the very common one, is to run this command via ssh:
$ ssh user#example.com 'bash -l -c "echo test"'
Password:
stdin: is not a tty
test
It happens because ssh does not allocate a terminal when called with a command as a parameter (one should use -t option for ssh to force the terminal allocation in this case).
Why it did not work without -l?
As correctly stated by #Cyrus in the comments, the list of files which bash loads on start depends on the type of the session. E.g. for login shells it will load /etc/profile, ~/.bash_profile, ~/.bash_login, and ~/.profile (see INVOCATION in manual bash(1)), while for non-login shells it will only load ~/.bashrc. It seems you defined your http_proxy variable only in one of the files loaded for login shells, but not in ~/.bashrc. You moved it to ~/.wgetrc and it's correct, but you could also define it in ~/.bashrc and it would have worked.
in your .profile, change
mesg n
to
if `tty -s`; then
mesg n
fi
I ended up putting the proxy configuration in the wgetrc. There is now no need to execute the script on a login shell anymore.
This is not a real answer to the actual problem, but it solved mine.
If you run into this problem check if you are getting all the environment variables set as you expect. Thanks to Cyrus for putting me to the right direction.

Resources