Can't populate variable from command in Bash - linux

I'm new to bash scripting, and I'm working on a script where the user enters a username and gets a list of the associated information from /etc/passwd. Unfortunately, I seem to be having trouble populating a variable from a command. The error message I'm getting suggests the if statement isn't being entered into, but I'm not sure why.
The script currently looks like this:
#!/bin/bash
#readifs
FILE=/etc/passwd
read -p "Enter a username > " user_name
file_info=$(grep "^$user_name:" $FILE)
if [ -n "$file_info" ]; then
IFS=":" read user pw uid gid name home shell <<< "$file_info"
echo "User = '$user'"
echo "UID = '$UID'"
echo "GID = '$GID'"
echo "Full Name = '$name'"
echo "Shell = '$shell'"
else
echo "No such user '$user_name'" > &2
exit 1
fi
When I run it, using a valid username, I get the following two lines:
readifs.sh: line 20: syntax error near unexpected token `&'
readifs.sh: line 20: ` echo "No such user '$user_name'" > &2'
I'm pretty sure I'm missing something obvious, or doing something bash doesn't allow but I'm too new to catch. Can anyone point out and correct the error in my script?

Thank you to Charles Duffy for all the great feedback on not just this script, but bash scripting and Stack Overflow in general.
I was able to fix the script as I wanted. I removed the ^ and : from the file_info line, which was stopping the grep command from finding the line I wanted. I also renamed $UID and $GID to use lower case letters, and removed the space in "> &2".
Thank you again for your assistance.

Related

Xterm: How can I direct input from one terminal tab to another?

As I am a beginner coder, I apologize in advance for improper terminology.
This is the main script which calls the script ping.sh in a new tab.
#!/bin/bash
echo "The script is running!"
rm ping.txt
echo "Enter your desired IP address:"
read ADDRESS
osascript -e 'tell application "System Events" to tell application "Terminal"
do script "./ping.sh"
end tell'
echo "The script has ended!"
exit 0;
So, as I said the script ping.sh is called now. It goes like this.
#!/bin/bash
echo "Welcome to the new tab!"
ping -c 3 $ADDRESS > ping.txt
exit 0
The problem I have is that the read input from the first tab isn't recognizable in the second tab. Is there a way to solve this? I am probably missing a linking constructor or something like that. Please help!
I have no idea what osascript is, or how it works, but it might be helpful to know that shell scripts can access command line arguments with the special variables $1, $2, $3, etc.
This means you can rewrite your ping.sh script like so:
#!/bin/bash
echo "Welcome to the new tab!"
ping -c 3 "$1" > ping.txt
exit 0
And then call it like so:
#!/bin/bash
echo "Enter your desired IP address:"
read ADDRESS
./ping.sh "$ADDRESS"
Otherwise, to make sure subsequent commands have access to the same environment variables, you have to export them. From help export:
export: export [-fn] [name[=value] ...] or export -p
Set export attribute for shell variables.
Marks each NAME for automatic export to the environment of subsequently
executed commands. If VALUE is supplied, assign VALUE before exporting.
To make your original ping.sh work you could do the following:
#!/bin/bash
echo "Enter your desired IP address:"
read ADDRESS
export ADDRESS
./ping.sh

How to compare user input with array and execute command?

I am trying to write a script to login into different systems by providing system name as input and making it variable by read option. However when i try to compare it with defined Array it's throwing me error and stating command not found.
Succeeded in making use input as variable but not able to compare it properly with defined array.
Below is the code i have written.
#!/bin/bash
cluster=("namico1c.mylabserver.com","namico2c.mylabserver.com")
echo "Please enter a Cluster Name to login: "
read clname
for item in ${cluster[#]};do
echo ${item};
if ["${clname}"="${item}"]; then
ssh test#$clname
else
echo "Cluster is not correct"
fi
done
[test#namico3c ~]$ ./test.sh
Please enter a Cluster Name to login:
namico1c.mylabserver.com
namico1c.mylabserver.com,namico2c.mylabserver.com
./test.sh: line 7: [namico1c.mylabserver.com=namico1c.mylabserver.com,namico2c.mylabserver.com]: command not found
Cluster is not correct
alternative:
#!/bin/bash
cluster=("namico1c.mylabserver.com" "namico2c.mylabserver.com")
select clname in "${cluster[#]}"; do
ssh test#$clname
break
done

Linux bash script - For loops issues

I'm working on a bash script that will add users in a batch process. This code goes as follows:
#!/bin/bash
# A script that creates users.
echo "This is a script to create new users on this system."
echo "How many users do you want to add?"
read am
echo " "
for i in {0..$am..1}
do
echo "Enter a username below:"
read usern
sudo useradd $usern
sudo passwd $usern
echo " "
echo "User $am '$usern' added."
done
In this case, I wanted to make 4 users. I went through and entered the username "callum3" and set the password as "1234" for ease of login. Once I input everything (correctly, may I add) the terminal window displays the following.
User 4 'callum3' added.
This shows that my for loop isn't actually working, when I can see nothing wrong with it. I have tried using a while loop with no luck there either.
Am I making a rookie mistake here or is there something deeper going on?
Although I suspected it, for a better understanding on what could be wrong with your script I pasted it in shellcheck.net. That the problem is in the line:
for i in {0..$am..1}
Bash doesn't support variables in brace range expansions. That is, you cannot use a variable in an expression like {..}.
Instead, use seq. With seq $var you get a sequence from 1 (default) to $var:
for i in $(seq "$am")
I feel like I'm missing something in that nobody has suggested an arithmetic for loop:
for ((i=0; i<am; i++)); do
…
done
This has the particular benefit in bash of being both readable and not requiring a subshell.
You can use:
for i in `seq 0 $((am-1))`
do
...
done
Sequence will start from 0 and end at $am-1

Need to fix if else script

I wanted to make a script which should take an argument and check if it is equal to a given word and then display a message accordingly. I use the bash shell of ubuntu OS. I tried something as per the tutorial - http://www.tech-recipes.com/rx/209/bournebash-shell-scripts-string-comparison/ and it failed.
#!/bin/bash
if ["$1"=="password"]
then
echo correct password
else
echo wrong password
fi
bash Script.sh password.
error message is -
[password=password]: command not found.
How to fix it ?
Whitespaces:
#!/bin/bash
if [ "$1" == "password" ]
then
echo correct password
else
echo wrong password
fi
The expression after the if is actually a command, and commands are delimited by whitespaces. So your command is ["$1"=="password"] that expands to [password==password], that obiously does not exist (/usr/bin/[password==password] anyone?).
In my corrected code, the command is [ (yes, there is a /bin/[) and the rest of the line are the arguments.
See man test for details (test is a kind-of-alias for [).

Bash script to capture input, run commands, and print to file

I am trying to do a homework assignment and it is very confusing. I am not sure if the professor's example is in Perl or bash, since it has no header. Basically, I just need help with the meat of the problem: capturing the input and outputting it. Here is the assignment:
In the session, provide a command prompt that includes the working directory, e.g.,
$./logger/home/it244/it244/hw8$
Accept user’s commands, execute them, and display the output on the screen.
During the session, create a temporary file “PID.cmd” (PID is the process ID) to store the command history in the following format (index: command):
1: ls
2: ls -l
If the script is aborted by CTRL+C (signal 2), output a message “aborted by ctrl+c”.
When you quit the logging session (either by “exit” or CTRL+C),
a. Delete the temporary file
b. Print out the total number of the commands in the session and the numbers of successful/failed commands (according to the exit status).
Here is my code so far (which did not go well, I would not try to run it):
#!/bin/sh
trap 'exit 1' 2
trap 'ctrl-c' 2
echo $(pwd)
while true
do
read -p command
echo "$command:" $command >> PID.cmd
done
Currently when I run this script I get
command read: 10: arg count
What is causing that?
======UPDATE=========
Ok I made some progress not quite working all the way it doesnt like my bashtrap or incremental index
#!/bin/sh
index=0
trap bashtrap INT
bashtrap(){
echo "CTRL+C aborting bash script"
}
echo "starting to log"
while :
do
read -p "command:" inputline
if [ $inputline="exit" ]
then
echo "Aborting with Exit"
break
else
echo "$index: $inputline" > output
$inputline 2>&1 | tee output
(( index++ ))
fi
done
This can be achieved in bash or perl or others.
Some hints to get you started in bash :
question 1 : command prompt /logger/home/it244/it244/hw8
1) make sure of the prompt format in the user .bashrc setup file: see PS1 data for debian-like distros.
2) cd into that directory within you bash script.
question 2 : run the user command
1) get the user input
read -p "command : " input_cmd
2) run the user command to STDOUT
bash -c "$input_cmd"
3) Track the user input command exit code
echo $?
Should exit with "0" if everything worked fine (you can also find exit codes in the command man pages).
3) Track the command PID if the exit code is Ok
echo $$ >> /tmp/pid_Ok
But take care the question is to keep the user command input, not the PID itself as shown here.
4) trap on exit
see man trap as you misunderstood the use of this : you may create a function called on the catched exit or CTRL/C signals.
5) increment the index in your while loop (on the exit code condition)
index=0
while ...
do
...
((index++))
done
I guess you have enough to start your home work.
Since the example posted used sh, I'll use that in my reply. You need to break down each requirement into its specific lines of supporting code. For example, in order to "provide a command prompt that includes the working directory" you need to actually print the current working directory as the prompt string for the read command, not by setting the $PS variable. This leads to a read command that looks like:
read -p "`pwd -P`\$ " _command
(I use leading underscores for private variables - just a matter of style.)
Similarly, the requirement to do several things on either a trap or a normal exit suggests a function should be created which could then either be called by the trap or to exit the loop based on user input. If you wanted to pretty-print the exit message, you might also wrap it in echo commands and it might look like this:
_cleanup() {
rm -f $_LOG
echo
echo $0 ended with $_success successful commands and $_fail unsuccessful commands.
echo
exit 0
}
So after analyzing each of the requirements, you'd need a few counters and a little bit of glue code such as a while loop to wrap them in. The result might look like this:
#/usr/bin/sh
# Define a function to call on exit
_cleanup() {
# Remove the log file as per specification #5a
rm -f $_LOG
# Display success/fail counts as per specification #5b
echo
echo $0 ended with $_success successful commands and $_fail unsuccessful commands.
echo
exit 0
}
# Where are we? Get absolute path of $0
_abs_path=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
# Set the log file name based on the path & PID
# Keep this constant so the log file doesn't wander
# around with the user if they enter a cd command
_LOG=${_abs_path}/$$.cmd
# Print ctrl+c msg per specification #4
# Then run the cleanup function
trap "echo aborted by ctrl+c;_cleanup" 2
# Initialize counters
_line=0
_fail=0
_success=0
while true
do
# Count lines to support required logging format per specification #3
((_line++))
# Set prompt per specification #1 and read command
read -p "`pwd -P`\$ " _command
# Echo command to log file as per specification #3
echo "$_line: $_command" >>$_LOG
# Arrange to exit on user input with value 'exit' as per specification #5
if [[ "$_command" == "exit" ]]
then
_cleanup
fi
# Execute whatever command was entered as per specification #2
eval $_command
# Capture the success/fail counts to support specification #5b
_status=$?
if [ $_status -eq 0 ]
then
((_success++))
else
((_fail++))
fi
done

Resources