counter=0
if [ ${counter} < 10 ]; then
echo 10
fi
error : no such file or directory.
What wrong in there ?
Instead of using the < symbol (which is for shell redirection) you need to use the -lt option to test.
counter=0
if [ ${counter} -lt 10 ]; then
echo 10
fi
With the <, the shell is actually trying to read in a file named 10 to stdin of the command [ ${counter} (aka test ${counter}).
man test is one of those things I constantly refer back to just to reassure myself.
Related
I want to create a directory in which there is a bunch of text files using a for loop. Here is my code:
#!/bin/bash
echo "enter the nums: "
read num1 num2
for (( counter=0; counter<$num2; counter++ ))
do
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ];
then
mkdir $num1 && touch $num1/$num1$num2.txt
echo "$num1""$num2" > $num1/$num1$num2.txt
else
echo "you weren't supposed to do that"
fi
done
What I want to happen if for example the user entered: "2 9"
Make a directory called 2
In it make text files called 290.txt, 291.txt, 292.txt... up till 299.txt.
Instead, what happens right now is it makes the directory and gives an error that the directory already exists. I don't know the next step, please help.
The biggest problem here is that you're doing things inside the loop that really only should be done once. Specifically, the error you're getting is because it tries to create the directory every time through the loop, but you can only create it once. Also, if the user enters too large a number, it'll print multiple error messages (e.g. if num2 is entered as 500, it'll print 500 error messages). You need to do both the error check and creating the directory once, before the loop.
A second problem is that you don't add $counter to the filename, so if the user enters "2 9", it'll create a file named 29.txt nine times.
You also have some more minor issues: in general, error messages should be printed to standard error instead of standard output (you can redirect them with >&2), and if there's an error the script should exit with a nonzero status. Also, you should (almost always) put double-quotes around variable references, to avoid weird results if the variables are blank or contain whitespace or some other things. You also don't need to touch files before writing into them (using > somefile will create the file if it doesn't exist).
With these things fixed (and some stylistic tweaks), here's what I get:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if ! [ "$num1" -lt 10 ] || ! [ "$num2" -lt 10 ]; then
echo "you weren't supposed to do that" >&2 # message send to stderr
exit 1 # exit with error status
fi
mkdir "$num1" || exit $? # if mkdir fails, exit with its error status
for (( counter=0; counter<$num2; counter++ )); do
echo "${num1}${num2}" > "${num1}/${num1}${num2}${counter}.txt"
done
BTW, the ! [ "$num1" -lt 10 ] tests may look a little weird; why not just use [ "$num" -ge 10 ]? I did it that way in case $num1 and/or $num2 isn't a valid number, in which case both -lt and -ge tests would fail; using a negated test makes that an error rather than a success.
I'm not fluent in bash or anything, but it looks like mkdir $num1 is called on every loop. Find out first if the directory exists.
Here you are! change the if and for statement parent and child:
#!/bin/bash
echo "enter the nums: "
read num1 num2
if [ $num1 -lt 10 ] && [ $num2 -lt 10 ]; then
mkdir $num1
for i in $(seq 0 $num2); do
touch $num1/$num1$num2$i.txt
echo "$num1""$num2""$i" > $num1/$num1$num2$i.txt
done
else
echo "you weren't supposed to do that"
fi
I am trying to compare the files in two directories but I am having trouble getting my stat command to work properly, I can get it to work from the command line using the same syntax as I have here.
# Usage: compdir <base_dir> <modified_dir>
# Handle MODIFIED and REMOVED files
for i in "${arr1[#]}"
do
REMOVED=1
for j in "${arr2[#]}"
do
if [ $i = $j ]; then
# take time stamps
dir1="$1"
dir2="$2"
stamp1=stat --format %Y "$i" <--------- THIS LINE
stamp2=stat --format %Y "$j"
if [[ $stamp1 > $stamp2 ]] ; then
echo "$j MODIFIED"
fi
REMOVED=0
break
fi
done
if [ $REMOVED -eq 1 ]; then
echo $i REMOVED
fi
done
# handle NEW files
for j in "${arr2[#]}"
do
NEW=1
for i in "${arr1[#]}"
do
if [ $j = $i ]; then
NEW=0
break
fi
done
if [ $NEW -eq 1 ]; then
echo "$j NEW"
fi
done
On the line marked with a <------- and the line below I get the error --format: command not found. I am assuming this is because I am in the base directory and not in the subdirectories. Since the arguments passed are the names of the directories I've tried doing something like "$1/$i" to get the line to work but have had no luck.
You cannot just assign a command to a variable, you have to do it in a subshell using $() or ``. Like here:
Option 1:
stamp1=$(stat --format %Y "$i")
Option 2:
stamp1=`stat --format %Y "$i"`
I personally prefer option 1 (subshell)
Addendum: As stated in the comment by sp asic (thx), use $() as the backticks are the legacy syntax, see: http://mywiki.wooledge.org/BashFAQ/082
I am writing a small .sh program in bash.
The problem is extremely simple, i.e, to dind the primefactors of a number.
What I've done is written a .sh file to check if a number is prime or not.
Here is the code for that :
if [ $# -ne 1 ]; then
exit
fi
number=$1
half=$(($number / 2))
for (( i=2;i<$half;i++ ))
do
rem=$(($number % $i))
if [ $rem -eq 0 ]; then
echo "0"
exit
fi
done
echo "1"
And the second .sh file to generate prime factors :
clear
echo "Enter number : "
read number
half=$(($number / 2))
for(( i=1;i<=$half;i++ ))
do
rem=$(($number % $i))
if [ $rem -eq 0 ]; then
ok=`prime.sh $rem`
if [ "$ok" == "1" ]; then
echi $i
fi
fi
done
This line ,
ok=`prime.sh $rem`
gives the following error :
primefactor.sh: line 10: prime.sh: command not found
So, is it not possible to divide a program into smaller modules and use it in the other modules like other programming languages ?
Some help on how to achieve this will be helpful.
primefactor.sh: line 10: prime.sh: command not found
...means that prime.sh is not in your PATH, or is not executable. There are a few ways you can remedy this:
First, ensure that the +x bit is set:
chmod +x prime.sh
...then, add it to your PATH:
PATH=.:$PWD
...or invoke it directly:
ok=$(./prime.sh)
By the way, names ending in .sh are appropriate for POSIX sh libraries, not bash scripts (which typically aren't valid POSIX sh scripts anyhow). You don't run ls.elf; you should run prime, not prime.sh, for the same reasons.
That said, if your goal is just to split your code amongst multiple files, a library might be the right thing. Using subshells (which fork an existing shell instance) is much more efficient than spawning subprocesses (which involve both a fork and an exec).
For instance, you could write prime.bash:
check() {
local number half i rem
number=$1
half=$((number / 2))
for (( i=2; i<half; i++ )); do
rem=$((number % i))
if (( rem == 0 )); then
echo "0"
return
fi
done
echo "1"
}
...and then, in your primefactor script, read in that library and use the function it defined:
source prime.bash # read in the library
clear
echo "Enter number : "
read number
half=$((number / 2))
for(( i=1;i<=half;i++ ))
do
rem=$((number % i))
if (( rem == 0 )); then
ok=$(check "$rem")
if [[ $ok = 1 ]]; then
echo "$i"
fi
fi
done
Call your script like this:
ok=`./prime.sh $rem`
I'm trying to delete a large amount of files from my computer, and I'm trying to write a bash script to do so using the rm command. What I want to know is how to do equality in bash, and why my code (posted below) won't compile. Thank you for your help!
#!/bin/bash
# int-or-string.sh
b="0000"
c="linorm"
f=500
e1=2
e2=20
e3=200
e4=2000
for i in {0..10000}
do
a=$(($f*$i))
if ["$i" -eq "$e1"]
then
b="000"
echo $b$
fi
if ["$i" -eq "$e2"]
then
b='00'
fi
if ["$i" -eq "$e3"]
then
b='0'
fi
if ["$i" -eq "$e4"]
then
b =''
fi
if [bash$ expr "$i" % "$e3$ -ne 0]
then
d = $b$c$a
rm d
fi
done
Shell scripts aren't compiled at all.
You need spaces after your [ and before your ].
if [ "$i" -eq "$e1" ]
There's an errant bash$ in there you probably don't want at all. It should probably be a $() operator:
if [ $(expr "$i" % "$e3") -ne 0 ]
You can't have spaces around the = in bash. For example, change b ='' to b='' and d = $b$c$a to d=$b$c$a.
echo $b$ looks like it should be echo $b.
Shell script does not compile it is a scripting language.
Try to fix this line :
if [bash$ expr "$i" % "$e3$ -ne 0]
Make it like below :
if [ $(expr "$i" % "$e3$") -ne 0 ]
You need spaces around the square brackets. The [ is actually a command, and like all commands needs to be delineated by white space.
When you set values for variables in shell, you do not put spaces around the equals signs.
Use quotation marks when doing comparisons and setting values to help delineate your values.
What happens if none of the if conditions are true, and $b isn't set.
What is the logic behind this code. It seems to be a bunch of random stuff. You're incrementing $ from 1 to 10000, but only setting the value of $b on only four of those values. Every 200 steps, you delete a file, but $b may or may not be set even though it's part of the file name.
Did you write this program yourself? Did you try to run it? What errors were you getting? Did you look at the lines referenced by those errors. It looks like you included the bash$ prompt as part of the command.
There were plenty of errors, and I've cleaned most of them up. The cleaned up code is posted below, but it still doesn't mean it will do what you want. All you said is you want to delete "a large amount of files" on your computer, but gave no other criteria. You also said "What I want to know is how to do equality in bash" which is not the question you stated in you header.
Here's the code. Note the changes, and it might lead to whatever answer you were looking for.
#!/bin/bash
# int-or-string.sh
b="0000"
c="linorm"
f=500
e1=2
e2=20
e3=200
e4=2000
for i in {0..10000}
do
a=$(($f*$i))
if [ "$i" -eq "$e1" ]
then
b="000"
elif [ "$i" -eq "$e2" ]
then
b='00'
elif [ "$i" -eq "$e3" ]
then
b='0'
elif [ "$i" -eq "$e4" ]
then
b=''
fi
if ! $(($i % $e3))
then
d="$b$c$a"
rm "$d"
fi
done
ERRORS:
Spaces around the [ and ]
The rm "$d" command was originallyrm dwhich would just remove a file namedd`.
if/then statement converted to if/else if.
Rewrote [ $(expr "$1" % "$e3") -ne 0 ].
No need for expr since BASH has $((..)) syntax.
No need for test command ([) since if automatically evaluates zero to true and non-zero to false.
Added quotes.
I am attempting to port a shell script to run on GNU for Windows, which provides a bash shell for Windows. I've run into an issue:
while [ $no -le $number ]
doesn't seem to register. As a test, I tried
no=1
number=10
echo debug1
while [ $no -le $number ]
do
echo debug2
no=$((no+1))
done
echo debug3
This returns debug1 and debug3, so it doesn't seem to even enter the loop.
no=1
echo debug1
while [ $no -le 10 ]
do
echo debug2
no=$((no+1))
done
echo debug3
This, on the other hand, works, and gives me debug1, 10 debug2s, and debug3.
I'm guessing it's a syntax error, but I'm not sure how to fix this. Any suggestions?
That works fine (as it should) under bash in Linux, and even under the relatively ancient bash I have in CygWin:
pax> number=10
pax> no=1 ; while [ $no -le $number ] ; do echo $no ; no=$((no+1)) ; done
1
2
3
4
5
6
7
8
9
10
My only suggestion is to print out the two variables before you attempt to enter the while loop, and after the no=$((no+1)) line, in case there's something wrong, something like:
echo "[$no] [$number]"
You may also want to put a set -x at the top of your script as it will output each interpreted line before trying to execute it.