Nothing happens on bourne shell script [closed] - linux

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I coded code that print decimals in 1 to 100.
and I executed this code, but Nothing happens...
I want to know what's the problem in my code
Please understand with my not good English
enter image description here
#!/bin/sh
i=2
while [ $i -le 100]
do
c=0
j=1
while [ $j -lt $i ]
do
if [ $(( $i % $j )) -eq 0 ]
then
c=$(( $c+1 ))
fi
j=$(( $j+1 ))
if [ $c -eq 0 ]
then
echo i
fi
done
i=$(( $i+1 ))
done

As shown by StephaneVeyret, the shebang shouldn't have spaces .
The purpose of the script seems to be printing numbers sequentially. If that is the case, you might want to take a look at a simple code like this :
#!/bin/bash
for i in {1..100}
do
printf "$i\n"
done

There should not be space in your shebang, between ! and /bin/sh. The first line of your script should be:
#!/bin/sh
(instead of #! /bin/sh)
Also, please note that if you really want to use Bourne shell, it should actually be:
#!/bin/bash
EDIT1: more problems:
As you want to use bash, you'd better use double brackets in your conditional statement, i.e.:
if [[ $j -le $i ]]
because they are more “powerful” in bash.
For this particular expression, you could also do:
if (( j <= i ))
Now, be careful to put spaces after [[ and (( and before ]] and )), and around operators:
$j=$(( $i + 1 ))
There seem to also be algo problems, but I don't have time to re-create your script and will check them only if I can copy-paste on my computer. Can you please paste an updated version of your script in your question? That would be easier for us than working on an image.
EDIT2: re your last post:
Line 4: there is a missing space before the closing bracket:
while [ $i -le 100 ]
Now, if you want to know what the script is doing, you can add the following command, for example, just after the shebang:
set -x

There are a few technical problems with your code, and also (if I understand what you are trying to do correctly) some logical errors in the design.
I'll share a few comments about how I looked at this. First thing to note is that the code you show is inconsistently indented, which means it is hard to see the logical structure of the code by simply looking at it. I loaded this into my usual editor (VSCode) which has a handy "Format Document" feature (many other code editors have similar auto-format features), and that produced the following:
#!/bin/sh
i=2
while [ $i -le 100]; do
c=0
j=1
while [ $j -lt $i ]; do
if [ $(($i % $j)) -eq 0 ]; then
c=$(($c + 1))
fi
j=$(($j + 1))
if [ $c -eq 0 ]; then
echo i
fi
done
i=$(($i + 1))
done
Note how this properly shows the nesting of statements within the inner while loop, in contrast to the original code. Even if you don't have access to a fancy editor, it is worthwhile spending the time to ensure that your code is manually indented correctly. It will save you a lot of time later in understanding and debugging the code.
Note also that the echo i line towards the end of the code should be echo $i if you want to print the value of the i variable.
When I try to run this code, it actually gives me an error telling me there's a missing ] in line 4. You need a space after the 100 here (as also pointed out by Stéphane Veyret):
while [ $i -le 100 ]; do
Next up, I added extra echo lines at various points in the code just to debug it. For example, the first thing I tried was to add the following line just before the final done line (the last line of the code)
echo "i=" $i
When I run the code I see this message printed multiple times, which tells me that the loop is running and that i is getting incremented from 2 to 100 as expected. I then added further echo lines at places within the while loop to check the values of other variables too.
These investigations highlighted a problem in your logic in the line
if [ $(($i % $j)) -eq 0 ]; then
The variable j is set to 1 at the start of each pass of the outer while loop. Any number modulus 1 is equal to zero, so this test will always be true when j is 1. This means that c will always be incremented the first time this code is encountered in each pass of the outer while loop (c=$(($c + 1))). And that means that the following test (if [ $c -eq 0 ]; then) will never be true.
I hope that gives you some ideas of what the problems are and how to go about further testing and debugging the code to make it do what you want.

Related

Concatenate Unknown Multiple Text Files While Inserting a Divider After the Contents of Each File Shell Script

For this problem in shell script, I need to accept an unknown multiple number of texts files and concatenate them. But I need to put a divider such as (-----) after the contents of each file. There also needs to be an exit code for the script where if it succeeds then it prints out the correct output. But if it fails it needs to print out this help message, "Usage..." (It's long and the help message itself isn't that important.)
I already figured out how to accept multiple number of text files to concatenate them but the problem is the divider really.
I also figured out how to show the help message using cat but the problem is that I don't know what conditions I need to put for the if and else statement because the conditions itself isn't working as intended.
For example, if it can find txt files and print the output it works as intended, however if there is no txt file it doesn't print the help message.
I am pretty sure it has to be some kind of loop statement for the divider to work but the problem is I have no idea how to do it. Not even sure of the conditions.
# Don't know what condition to put for the if here
if [ $? -eq 0 ]
then
# Don't know what condition to put for the for loop here
for j in "$?"
do
cat *.txt
echo "-----"
done
exit 0
else
cat <<< "Usage..." >&2
exit 1
fi
Let's say we have 2 text files.
one.txt has:
Hello
two.txt has:
Happy
Then if the code runs as intended the output should be:
Hello
-----
Happy
-----
Something amongst those lines ?
#!/bin/bash
for param in $#; do
if [[ -f $param ]]; then
cat $param
echo "-----"
else
echo "wrong parameter passed! usage:..."
exit 1
fi
done
exit 0
To explain quickly :
for param in $#; do
Iterate over every parameter passed to the script
[[ -f $param ]]
This checks whether the parameter is an existing file

"if[ 0 -lt 1] command not found" error in shell script [duplicate]

This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
Why is whitespace sometimes needed around metacharacters?
(5 answers)
Closed 5 years ago.
I got error message as if[ 0 -lt 1] command not found. I just started shell scripting. I don't know what's wrong in my code:
if[ $# -lt 1 ]
then
echo "Give a number as a parameter. Try again."
else
n=$1
sum= 0
sd=0
while[ $n -gt 0 ]
do
sd=`expr $n % 10`
sum=`expr $sum + $sd`
n=`expr $n / 10`
done
echo "Sum of digits for $1 is $sum"
You need a space between if and [. You'll have the same issue after while.
The [ ] notation is kind of an oddity in shell. It looks like part of the language, but it actually isn't. The if and while words always need to be followed by whitespace and then a command, which is executed, and considered to be true or false according to whether its exit code is zero or nonzero.
So there is actually a command named [ which evaluates the conditions given on its command line, and terminates with an exit code according to whether the condition evaluated true or false. You can see an executable in the /usr/bin directory on most systems (though the shell usually has a built-in version for efficiency). It works the same as test.
Also, keep in mind that if needs a matching fi after the else clause.
Check out https://www.shellcheck.net/ (Open Source - free)
It allows you to check your shell scripts before running them. You just copy your shell script into the browser and do a syntax check, you can also run locally.
Why?
Well, this I think will really help you learn what you are doing wrong.
i.e. Fix spacing errors etc.
Example Output:
$ shellcheck myscript
Line 1:
if[ $# -lt 1 ]
^-- SC1046: Couldn't find 'fi' for this 'if'.
^-- SC1073: Couldn't parse this if expression.
^-- SC1069: You need a space before the [.
Line 8:
while[ $n -gt 0 ]
^-- SC1069: You need a space before the [.
Line 15:
^-- SC1047: Expected 'fi' matching previously mentioned 'if'.
^-- SC1072: Expected 'fi'. Fix any mentioned problems and try again.
$

Shell Script working with multiple files [duplicate]

This question already has answers here:
How to iterate over arguments in a Bash script
(9 answers)
Closed 5 years ago.
I have this code below:
#!/bin/bash
filename=$1
file_extension=$( echo $1 | cut -d. -f2 )
directory=${filename%.*}
if [[ -z $filename ]]; then
echo "You forgot to include the file name, like this:"
echo "./convert-pdf.sh my_document.pdf"
else
if [[ $file_extension = 'pdf' ]]; then
[[ ! -d $directory ]] && mkdir $directory
convert $filename -density 300 $directory/page_%04d.jpg
else
echo "ERROR! You must use ONLY PDF files!"
fi
fi
And it is working perfectly well!
I would like to create a script which I can do something like this: ./script.sh *.pdf
How can I do it? Using asterisk.
Thank you for your time!
Firstly realize that the shell will expand *.pdf to a list of arguments. This means that your shell script will never ever see the *. Instead it will get a list of arguments.
You can use a construction like the following:
#!/bin/bash
function convert() {
local filename=$1
# do your thing here
}
if (( $# < 1 )); then
# give your error message about missing arguments
fi
while (( $# > 0 )); do
convert "$1"
shift
done
What this does is first wrap your functionality in a function called convert. Then for the main code it first checks the number of arguments passed to the script, if this is less than 1 (i.e. none) you give the error that a filename should be passed. Then you go into a while loop which is executed as long as there are arguments remaining. The first argument you pass to the convert function which does what your script already does. Then the shift operation is performed, what this does is it throws away the first argument and then shifts all the remaining arguments "left" by one place, that is what was $2 now is $1, what was $3 now is $2, etc. By doing this in the while loop until the argument list is empty you go through all the arguments.
By the way, your initial assignments have a few issues:
you can't assume that the filename has an extension, your code could match a dot in some directory path instead.
your directory assignment seems to be splitting on . instead of /
your directory assignment will contain the filename if no absolute or relative path was given, i.e. only a bare filename
...
I think you should spend a bit more time on robustness
Wrap your code in a loop. That is, instead of:
filename=$1
: code goes here
use:
for filename in "$#"; do
: put your code here
done

Saving the arguments in different variables passed to a shell script

I need to save two command line arguments in two different variables and rest all in third variable.
I am using following code
while [ $# -ge 2 ] ; do
DirFrom=$1
Old_Ver=`basename $1`
shift
DirTo=$1
shift
pdct_code=$#
shift
done
This code is failing if I send more than three arguments . Please suggest how can I save 3rd 4th and so on variable in pdct_code variable.
You're not entering the loop when you have more than two arguments. You can bump the argument limit like so:
while [ $# -ge 3 ]; do
:
done
or better yet just parse your arguments without looping at all. For example:
DirFrom="$1"
Old_Ver=`basename "$1"`
DirTo="$2"
pdct_code="$*"
No loop or shifting is needed. Note that pdct_code may need to be an array, to preserve the exact arguments passed to your script.
if [ $# -ge 2 ]; then
DirFrom=$1
Old_Ver=$(basename "$1")
DirTo=$2
pdct_code="${#:3}"
# pdct_code=( "${#:3}" )
done

BASH scripting: taking in multiple flags

I have this issue where I am doing a menu sort of program where users are supposed to type in different numbers depending on what operation(s) the want to do.
Right now I need to take in multiple flags to be placed in a command, but I can't seem to get it to work.
Catch: I cannot type in the flags directly into one variable and I will probably have to have this case list to be able to quit at will)
function fileCpy {
printf "Choices:\n1. Interactive copy, answer yes/no before doing the copy: \n2. Make backups of existing destination files\n3. Preserve file attributes\n4. Do a recursive copy\n0. Return to main menu\n"
#read choices
read $a
read $b
read $c
for choices in $choice ; do
case "$choices" in
1)
a=-i ;;
2)
b=--backup ;;
3)
c=-p ;;
#4)
#4= -R -v;;
0)
main;;
esac
echo "$a"
echo "$b"
echo "$c"
printf "\nType the name of the file you wish to copy/backup: "
read $fl1
printf "\nType the destination file: "
read $des1
cp $a $b $c $fl1 $des1
done
}
One problem is that read $a etc is wrong: you should write read a if you need the read at all. As it stands, the value read is stored in the variable with the name stored in $a.
Another problem is that it is far from clear to the innocent user that they're supposed to enter 3 lines of information before the script will continue, but the three read lines force that.
Another problem is that you don't read into $choice (via read choice) so the for loop has nothing to do.
Another problem is that your script will inherit the values of any environment variables that happen to be the same as the names of the variables you're using.
Another problem is that you don't quote the file names. It mostly won't matter unless you have a name that contains spaces or other similarly awkward characters.
A cosmetic issue is that the printf statement is ridiculously long. Use one printf per line. Or use echo. Stuff that scrolls off the RHS of the page is bad (though I don't regard 80 characters as a fixed length for lines, there's a quadratic penalty for lines that are longer than 80 — as (length-80)2 increases, the pain of the longer line goes up.
At another level altogether, the interface is modestly grotesque. As an exercise in shell scripting, it makes sense. As an exercise in how to design good shell scripts, it is a very bad design.
A design that might make sense is:
Set variables to empty: a=""; b=""; c=""; etc.
Offer a range of choices similar to those given now, but add an option to execute the command, and another to abandon ship.
Have a loop that reads choices, and sets flags.
When the user chooses execute, exit the loop and prompt for the file names.
If all's well, execute the command.
Note that you should check that the read commands work; if they don't, fail safe (don't damage anything).
Putting all those together (with some slight differences, but the same overall effect — witness the use of local for the variables):
fileCpy()
{
local a b c file dest
echo "Choices:"
echo "0. Return to main menu"
echo "1. Interactive copy, answer yes/no before doing the copy"
echo "2. Make backups of existing destination files"
echo "3. Preserve file attributes"
echo "4. Do a recursive copy"
echo "5. Execute the copy"
while printf "Your choice: " && read choice
do
[ -z "$choice" ] && return 1 # Empty input - failure
case "$choice" in
(0) return 0;;
(1) a="-i";;
(2) b="--backup";;
(3) c="-p";;
(4) d="-R";;
(5) break;;
(*) echo "Unrecognized response ($choice); please enter 0..5";;
esac
done
[ "$choice" != 5 ] && return 1 # EOF - failure
printf "Type the name of the file you wish to copy/backup: "
read file
[ -z "$file" ] && return 1 # Empty file name - failure
printf "Type the name of the destination file/directory: "
read dest
[ -z "$dest" ] && return 1 # Empty file name - failure
cp $a $b $c "$file" "$dest"
}
Test code:
echo "a=$a b=$b c=$c file=$file dest=$dest"
if fileCpy
then : OK
else echo "Failed"
fi
echo "a=$a b=$b c=$c file=$file dest=$dest"
The last block is a simple test harness. It reports on the values of the variables used inside the function, runs the function, reports if the function failed, and re-echoes the variables to demonstrate that they've not been set.
I would not use that interface unless you paid me to do so, but it more or less meets the goal of a training exercise.
if you want to do menu, you can use select construct (or case/esac). See here for info

Resources