Recently i got an assignment at school, where we are to write a small program in Bash Scripting Language.
This shell script is supposed to accept some Positional Parameters or Arguments on the command line.
Now, with the help of an if-else statement i check if the argument is present, if the argument is present the script does what it is supposed to do, but if the argument is not present - i display an error message, prompting the user to input an argument and pass the argument again to the same shell script...
Now, i want to know if this approach is good or bad in Bash Programming paradigm. I'm slightly suspicious that this might run too many tasks in the background that are kept open or that are never ended and keep on consuming memory... please help.
Here's a small snippet (assume the script name to be script1.bash):
#!/bin/bash
if [ $# -gt 0 ]; then
read -p "Please enter your name: " name
script1.bash $name
elif [ $# -eq 1 ]; then
echo "Your name is $1!"
fi
It's ... questionable :-)
The main problem is that you're spawning a subshell everytime someone refuses to input their name.
You would be better of with something like:
#!/bin/bash
name=$1
while [[ -z "$name" ]] ; do
read -p "Please enter your name: " name
done
echo "Your name is $name!"
which spawns no subshells.
Related
Hi I am doing a project where I must write a bash script calculator that allows you to perform multiple arithmetic operations and also to the power of. I have done the basic bit but have been told I must make it non interactive and was just wondering how would I do this? My code has been provided below.
clear
bash
while [ -z "$arg1" ]
do
read -p "Enter argument1: " arg1
done
while [ -z "$arg2" ]
do
read -p "Enter argument2: " arg2
done
echo "You've entered: arg1=$arg1 and arg2=$arg2"
let "addition=arg1+arg2"
let "subtraction=arg1-arg2"
let "multiplication=arg1*arg2"
let "division=arg1 / arg2"
let "power=arg1**arg2"
echo -e "results:\n"
echo "$arg1+$arg2=$addition"
echo "$arg1-$arg2=$subtraction"
echo "$arg1*arg2=$multiplication"
echo "$arg1/$arg2=$division"
echo "$arg1^$arg2=$power"
I was thinking of trying to make it so that the user does not have to type in 2 numbers but I am still pretty new to bash and scripts as a whole so I am wondering how to do this.
Use getopts to process arguments to your script. Here is an example from this site. You could replace the lines from your first line to your echo "You've entered …" with modified code from the other answer I linked to. You might want to read the PARAMETERS section of man bash, in particular understand what the Special Parameters are (*, #, #, ?, -, $, !, and 0). You should know those cold if you are scripting bash.
An alternative is to use the builtin read. I would use getopts.
How do I create a Bash script that takes a file name as input? Then, if that file exists, it should print "File exists"; if not, print "File does not exist".
For example, if I ran ./do-i-exist.sh ./do-i-exist.sh, the output should be only 'File exists'
file="$1"
read answer
if [ $file != -$2 ]
then
echo "File exists"
else
echo "File does not exist"
fi
This is what I'm working with but is not working for me, whenever I add an extension like .sh, .txt or something similar it won't find the file.
The test if a file exists can be done like this
if [ -f "$file" ]
then
This tests for a regular file, not for other kinds of files like a directory.
This is how you can do it. Pass the name of the file while like ./do-i-exist.sh file_path.
if [ -f "$1" ]
then
echo "File Exists"
else
echo "File does not exist"
fi
First of all, I want to thank anyone and everyone who tried to help. After 3 hard working days, I found the answer, here it is:
#!/bin/bash
file="$#"
if [ -f $file ]
then
echo "File exists"
else
echo "File does not exist"
fi
Using this table:
Variable Name
Description
$0
The name of the Bash script
$1 - $9
The first 9 arguments to the Bash script
$#
Number of arguments passed to the Bash script
$#
All arguments passed to the Bash script
$?
The exit status of the most recently run process
$$
The process ID of the current script
$USER
The username of the user running the script
$HOSTNAME
The hostname of the machine
$RANDOM
A random number
$LINENO
The current line number in the script
I and other users were focused on using $1 from my understanding this refers to the first argument passed to the script but for some reason, it wasn't working since it needed to pass more inputs.
As from my previous comments I didn't have control over the input. The input was hidden in a locked file, and I needed to feed my script to it.
From what we know $0 is only used to check for the file names, $1 to get the first statement and $# will just take anything(I guess).
I know absolutely nothing about bash and it was the first time ever using it, which is why it took me 3 days to solve this puzzle. This was part of a CTF and just like me, many others may struggle in the future to understand or know how to make a script that will just adapt to a series of inputs from a second script.
This is how it was supported to work:
I was given access to a very restricted server and on this server, I was given the encrypted-file.sh file. This file was supposed to be fed to /path/to/myfile.sh then encrypted-file.sh would execute a second command to open a third locked file hiding a flag on it.
This only works with the right bash file using the right variables on it for encrypted-file.sh to run without errors, which is what I accomplished here.
I used a while loop because it made sense in my case because I really needed a file for the script to work.
restore_file="$1"
while [ ! -f "$restore_file" ]
do
echo "File not found: $restore_file"
echo "Please provide a valid file:"
read restore_file
done
As written above, $1 is the first argument given to the script. In this case if no argument is given or that is not a file, it will prompt again.
By the way, use -d instead of -f to check for a directory.
I have script to change the structure from a .txt file into a 'newusers' command readable structure, which is great for 1 file, but it means I have to edit said script everytime I need to use it with a new file. I've read about positional parameters, which I think it's what I need in this situation, but not sure how to apply that to achieve my goal.
What can I do to pass the arguments while executing the script so it works with every file without needing to edit the script further/each time?
So far my script works with bash script.sh, no arguments.
Code so far:
#!/bin/bash
clear
while IFS=: read -r userid username group; do
if [ "$group" = "group1" ]; then
groupid=2020
elif [ "$group" = "group2" ]; then
groupid=2040
else
echo "Unknown group"
fi
echo "$userid:password123::$groupid:$username:/home/$userid/bin/bash";
done #< users2.txt > newusers2.txt
Expected result:
bash script.sh oldfile1 newfile1
Cheers.
One of the routines I frequently use is a check for valid arguments passed when invoking scripts. Ideally, I'd like to make these, and other, similar, routines external functions that I could call from any script, for handling these more trivial processes. But, I'm having trouble retrieving the values I need from said function(s), without making the process more complicated.
I have tried using command substitution (e.g., echoing the output of the external function into a variable name local to the calling script), which seems to at least work with simpler functions. However, working with this file checking function, requires the read command in a loop, and, thus, user interactivity, which causes the script to hang when trying to resolve the variable that function call is stored in:
#!/bin/bash
# This is a simple function I want to call from other scripts.
exist(){
# If the first parameter passed is not a directory, then the input is
#+ invalid.
if [ ! -d "$1" ]; then
# Rename $1, so we can manipulate its value.
userDir="$1"
# Ask the user for new input while his input is invalid.
while [ ! -d "$userDir" ]; do
echo "\"$userDir\" does not exist."
echo "Enter the path to the directory: "
read userDir
# Convert any tildes in the variable b/c the shell didn't get to
#+ perform expansion.
userDir=`echo "$userDir" | sed "s|~|$HOME|"`
done
fi
}
exist "$1"
How can I retrieve the value of userDir in the calling script without adding (much) complexity?
You can have the exist function interact with the user over stderr and still capture the variable with command substitution. Let's take a simplified example:
exist() { read -u2 -p "Enter dir: " dir; echo "$dir"; }
The option -u2 tells read to use file descriptor 2 (stderr) for interacting with the user. This will continue to work even if stdout has been redirected via command substitution. The option -p "Enter dir: " allows read to set the prompt and capture the user input in one command.
As an example of how it works:
$ d=$(exist)
Enter dir: SomeDirectory
$ echo "$d"
SomeDirectory
Complete example
exist() {
local dir="$1"
while [ ! -d "$dir" ]; do
echo "'$dir' is not a directory." >&2
read -u2 -p "Enter the path to the directory: " dir
dir="${dir/\~/$HOME}"
done
echo "$dir"
}
As an example of this in use:
$ d=$(exist /asdf)
'/asdf' is not a directory.
Enter the path to the directory: /tmp
$ echo "new directory=$d"
new directory=/tmp
Notes:
There is no need for an if statement and a while loop. The while is sufficient on its own.
Single quotes can be put in double-quoted strings without escapes. So, if we write the error message as "'$dir' is not a directory.", escapes are not needed.
All shell variables should be double-quoted unless one wants them to be subject to word splitting and pathname expansion.
Right off the bat I'd say you can 'echo' to the user on stderr and echo your intended answer on stdout.
I had to rearrange a bit to get it working, but this is tested:
exist(){
# If the first parameter passed is not a directory, then the input is
#+ invalid.
userDir="$1"
if [ ! -d "$userDir" ]; then
# Ask the user for new input while his input is invalid.
while [ ! -d "$userDir" ]; do
>&2 echo "\"$userDir\" does not exist."
>&2 echo "Enter the path to the directory: "
read userDir
done
else
>&2 echo "'$1' is indeed a directory"
fi
echo "$userDir"
}
When I tested, I saved that to a file called exist.inc.func
Then I wrote another script that uses it like this:
#!/bin/sh
source ./exist.inc.func
#Should work with no input:
varInCallingProg=$(exist /root)
echo "Got back $varInCallingProg"
#Should work after you correct it interactively:
varInCallingProg2=$(exist /probablyNotAdirOnYourSystem )
echo "Got back $varInCallingProg2"
I'm totally new in writing shell scripts so I could use some help here.
I would like to write a script that when run with no parameters it just echo backs, and when it is given a data (.dat) file it displays the content of it.
Excuse me for my bad English,
R.
This script, when run with no parameters it just echo backs and when a filename is passed as argument, it displays the content of it:
#!/bin/sh
# Explanation - We use'$#' to count number of arguments.
if ! [ $# -gt 0 ]; then
# Explanation - Zeroth argument '$0' is scriptname itself. Print it.
cat "$0"
else
# Explanation - Print (cat) 1st argument.
cat "$1"
fi
NOTE: Since you've used 'minix' tag, I tested it on minix3. The script works well on minix as well as linux.