$user or $whoami not working in a bash shell script - bash

I am learning basic unix shell scripting now. I was trying a code from here to write my username but it is not working.
The code is:
#
# Script to print user information who is currently logged in , current date & time
#
clear
echo "Hello $USER"
echo "Today is \c ";date
echo "Number of user login : \c" ; who | wc -l
echo "Calendar"
cal
exit 0
I tried $whoami instead of $user, but still it is not showing my username. What can be the issue here? I am using vim editor in Ubuntu.

If $USER is not working try, $LOGNAME. If you have already learned about command substitution then you can use $(whoami) or $(id -n -u). Ref
\c in echo wont work unless you specify with -e (stands for enable interpretation of backslash escapes).
echo -e "Today is \c ";date
It seems you want to prevent the trailing new line character introduced by echo. Another way to achieve this is to just add -n. Then you don't need -e and \c.
echo -n "Today is "; date

I tried `$whoami`
What you probably mean to do is `whoami` or $(whoami).
See Command Substitution.

Related

How can I log the command used to start a bash script? [duplicate]

I know about $*, $#, "$#" and even ${1+"#"} and what they mean.
I need to get access to the EXACT command-line arguments string from a shell script. Please pay attention to the quotes in the example. Anything like "$#" saves parameters but removes quotes and I don't see how to recover from this.
Example:
./my-shell.sh "1 2" 3
And I need to retrieve the EXACT parameter string without any processing:
"1 2" 3
Any idea how to achieve this?
In bash, you can get this from the shell history:
set -o history
shopt -s expand_aliases
function myhack {
line=$(history 1)
line=${line#*[0-9] }
echo "You wrote: $line"
}
alias myhack='myhack #'
Which works as you describe:
$ myhack --args="stuff" * {1..10} $PATH
You wrote: myhack --args="stuff" * {1..10} $PATH
Also, here's a handy diagram:
But I need to exactly reproduce a user-entered command line to be able to programmatically re-execute it in case of failure
You don't need your exact command line for that; you can reconstruct an equivalent (even if not identical!) shell command yourself.
#!/usr/bin/env bash
printf -v cmd_q '%q ' "$#"
echo "Executed with a command identical to: $cmd_q"
...though you don't even need that, because "$#" can be re-executed as-is, without knowing what the command that started it was:
#!/usr/bin/env bash
printf "Program is starting; received command line equivalent to: "
printf '%q ' "$#"
printf '\n'
if [[ ! $already_restarted ]]; then
echo "About to restart ourselves:
exec "$#" # restart the program
fi

read input from stdin but not have it echo [duplicate]

I'm trying to interpret this block of code. Searched google to see what these commands mean and no luck. I put my interpretation of what each line/block means to me. If I am wrong, please correct me. I am new to unix commands. Code:
#!/bin/bash
# input 1st command line argument for the version.
export VERSION=$1
# if user didn't input a version, print the echo message and exit (not sure what -n means but I am assuming)
if [[ ! -n "$VERSION" ]]; then
echo "Missing Version"
exit 1
fi
# creating variable UNAME that tells who the person is (their name)
export UNAME='whoami'
# no idea what -s and -p mean but i think this prints the message "enter password for $UNAME" and stores it in a new variable named PASSWORD. the $UNAME will print whatever whoami said.
read -s -p "Enter password for $UNAME: " PASSWORD
echo ""
The -p flag issues a prompt before reading input into a variable
The -s flag stop the typed response from being shown (i.e. for a sensitive password)
More information is available here:
https://linuxhint.com/bash_read_command/
-p
prompt output the string PROMPT without a trailing newline before
attempting to read.
-s
do not echo input coming from a terminal.

Bash script stderr and stdout

First, i'm not sure if i'm formulating the question correctly.
So I have a bash script that delete a user in a system. The problem is, it is showing the err msg before the terminal code.
Here.
Code:
echo -e "\nCommencing user $UserName removal"
echo -e "Deactivating $UserName shell account ..." "$(chsh -s /usr/bin/false $UserName)"
echo -e "Listing group(s) user: " "$(groups $UserName)"
echo -e "Removing Crontab ... " "$(crontab -r -u $UserName)"
Here is the output:
Commencing user Test2 removal
chsh: unknown user: Test2
Deactivating Test2 shell account ...
groups: Test2: no such user
Listing group(s) user:
scripts/sudo.sh: line 332: /delete_Test2/crontab.bak: No such file or directory
Saving Crontab ...
The user to delete is Test2, which is this case "supposedly" does not exist (a different question for a different time). Now, shouldn't the stderr msg display next to the command or below it instead of above it?
Thanks in advance
When the shell executes the line echo -e "Deactivating $UserName shell account ..." "$(chsh -s /usr/bin/false $UserName)", here's the sequence of events:
The shell runs chsh -s /usr/bin/false Test2, with its stdout going to a capture buffer so the shell can use it later.
chsh discover's that "Test2" doesn't exist, and prints "chsh: unknown user: Test2" to its stderr. Since the shell didn't do anything special with its stderr, this goes directly to the shell's stderr, which is your terminal.
chsh exits
The shell takes the captured output (note: stdout, not stderr) from chsh (there wasn't any), and substitutes it into the echo command line, giving echo -e "Deactivating Test2 shell account ..." ""
Note that the error message gets printed at step 2, but the message about what's supposedly about to happen doesn't get printed until step 4.
There are several ways to solve this; generally the best is to avoid the whole mess of capturing the command's output, then echoing it. It's pointless, and just leads to confusions like this (and some others you haven't run into). Just run the command directly, and let its output (both stdout and stderr) go to their normal places, in normal order.
BTW, I also recommend avoiding echo -e, or indeed echo -anything. The POSIX standard for echo says "Implementations shall not support any options." In fact, some implementations do support options; others just treat them as part of the string to be printed. Also, some interpret escape sequences (like \n) in the strings to print, some don't, and some only do if -e is specified (violating the POSIX standard). Given how unpredictable these features are, it's best to just avoid such iffy situations, and either use printf instead (which is more complicated to use, but much more predictable), or (as in your case) just use a separate echo command for each line.
Also, you should almost always double-quote variable references (e.g. groups "$UserName" instead of groups $UserName), just in case they contain spaces, wildcards, etc.
Based on the above, here's how I'd write the script:
echo # print a blank line
echo "Commencing user $UserName removal"
echo "Deactivating $UserName shell account ..."
chsh -s /usr/bin/false "$UserName"
echo "Listing group(s) user: "
groups "$UserName"
echo "Removing Crontab ... "
crontab -r -u "$UserName"
Now, shouldn't the stderr msg display next to the command or below it instead of above it?
No, because the command is executed first and then its output is echoed. Perform the echo and command in two separate lines.
That's because chsh is writing to stderr before bash has a chance to write to stdout. Redirect stderr to stdout to get it right:
echo -e "Deactivating $UserName shell account ..." "$(chsh -s /usr/bin/false $UserName 2>&1)"
In general, it is better to check if the user exists before trying to deactivate it.

How to use up arrow key in shell script to get previous command

I am trying to write my console with some basic functionalities.
Here is what I am doing.
function help()
{
echo "add(a,b,...)"
}
function add()
{
arg=$(echo $1 | cut -d"(" -f2)
sum=0
for number in `echo ${arg} | sed -e 's/[,) ]\+/\n/g'` ; do
sum=$(($sum + $number))
done
echo $sum
}
while true
do
echo -n "mycon#avi>>"
read command
opt=$(echo "$command" | cut -d"(" -f1)
case $opt in
"exit"|"q")
exit
;;
"help")
help
;;
"add")
add $command
;;
esac
done
I am saving this file as mycon
when I run this script ./mycon
mycon#avi>>add(2,3)
5
mycon#avi>>
Now in this moment when I am pressing up arrow key, I want to get the above add(2,3) command. what is the way to do this ??
Thank You
Bash-only solution:
Change read command to read -e command so that bash will enable the readline library.
Add the command history -s "$command" to include the line read into the history.
Note that read command will delete trailing whitespace from the typed command before assigning the line to command, unless you invoke it with IFS set to the empty string. Also, read will normally treat backslashes as escape characters, which is usually undesirable; you can suppress that behaviour with the -r flag. Finally, you can get read to print the prompt, which will work better with readline, using the -p option. So your final sequence might look like this:
while IFS= read -e -p "mycon#avi>> " command; do
history -s "$command"
# ... process the command
done
(Using the read command as the condition in the while statement causes the while loop to terminate if the user enters an EOF character.)
For more information on read and history, use the built-in bash help command (help read / help history)

How to get exact command line string from shell?

I know about $*, $#, "$#" and even ${1+"#"} and what they mean.
I need to get access to the EXACT command-line arguments string from a shell script. Please pay attention to the quotes in the example. Anything like "$#" saves parameters but removes quotes and I don't see how to recover from this.
Example:
./my-shell.sh "1 2" 3
And I need to retrieve the EXACT parameter string without any processing:
"1 2" 3
Any idea how to achieve this?
In bash, you can get this from the shell history:
set -o history
shopt -s expand_aliases
function myhack {
line=$(history 1)
line=${line#*[0-9] }
echo "You wrote: $line"
}
alias myhack='myhack #'
Which works as you describe:
$ myhack --args="stuff" * {1..10} $PATH
You wrote: myhack --args="stuff" * {1..10} $PATH
Also, here's a handy diagram:
But I need to exactly reproduce a user-entered command line to be able to programmatically re-execute it in case of failure
You don't need your exact command line for that; you can reconstruct an equivalent (even if not identical!) shell command yourself.
#!/usr/bin/env bash
printf -v cmd_q '%q ' "$#"
echo "Executed with a command identical to: $cmd_q"
...though you don't even need that, because "$#" can be re-executed as-is, without knowing what the command that started it was:
#!/usr/bin/env bash
printf "Program is starting; received command line equivalent to: "
printf '%q ' "$#"
printf '\n'
if [[ ! $already_restarted ]]; then
echo "About to restart ourselves:
exec "$#" # restart the program
fi

Resources