What this symbol mean "#?" in ksh - ksh

What does "#?" mean in ksh script? e.g:
tt=03
while [ "$tt" !=' ' ];
do
tt=${tt#"?}
done
echo $tt
I will get nothing here. So what "#?" means in this scipt? Thank you.

Assuming corrected syntax on the while as downtheroad suggested (need blank after [ and before ]) and also omitting the " in tt=${tt#"?} (the omitted version is what you cite).
Also the test in the while condition needs to be against an empty string'', otherwise the loop does not terminate.
tt=${tt#?}
chops off the first character from the string.
See this test program (I added the 2 echos and the parens to be able to see the exact contents of tt in the loop before and after chopping:
tt=03
while [ "$tt" != '' ]
do
echo "A: (tt=$tt)"
tt=${tt#?}
echo "B: (tt=$tt)"
done
echo $tt
gives this result (note the empty last line from the last echo):
A: (tt=03)
B: (tt=3)
A: (tt=3)
B: (tt=)

Related

How do I pass arguments to a defined function in a shell script?

I'm trying to create a function wherein I pass two dates as arguments and get their day of the week out. I've gotten it to work by copying and pasting the parsing process for both input dates, but I'm trying to save space by condensing the process into a defined function. The input should look like so:
datematch.sh 01/03/1984 06/12/2008
But I keep getting error messages along the lines of:
./birthday_match.sh: line 9: ${$1:0:2}: bad substitution
./birthday_match.sh: line 9: ${$1:0:2}: bad substitution
The first person was born on:
The second person was born on:
Thus, they were born on the same day.
How am I substituting wrong? The full code is below.
#!/bin/bash
var1=$1
var2=$2
if [ "$#" -ne 2 ]; then
echo "illegal number of birthdays"
else
function get_dayname ()
{
mo=${$1:0:2}
dy=${$1:3:2}
yr=${$1:6:4}
combo="${mo}${dy}0000${yr}"
fulldate="$(date $combo 2> /dev/null)"
wkdy=${fulldate:0:3}
eval $wkdy
}
first=$(get_dayname "$var1")
second=$(get_dayname "$var2")
echo "The first person was born on: $first"
echo "The second person was born on: $second"
if [ "$first" == "$second" ]; then
echo "Thus, they were born on the same day."
else
echo "Thus, they were not born on the same day."
fi
fi
The parameter expansion syntax is incorrect:
mo=${$1:0:2}
should be
mo=${1:0:2}
Consider the simple case. Substituting the first argument as-is is either $1 or ${1}. In the second version, the curly brackets serve to separate the variable name or number from what follows.
If you wrote ${$1}, the intuitive meaning would be "treat the first argument as a name, and substitute the value of the variable with that name" .... but bash parameter expansion syntax doesn't allow that.

How to extend string to certain length

Hey basically right now my program gives me this output:
BLABLABLA
TEXTEXOUAIGJIOAJGOAJFKJAFKLAJKLFJKL
TEXT
MORE TEXT OF RANDOM CHARACTER OVER LIMIT
which is a result of for loop. Now here's what i want:
if the string raches over 10 characters, cut the rest and add two dots & colon to the end "..:"
otherwise (if the string has less than 10 characters) fill the gap with spaces so they're alligned
so on the example i provided i'd want something like this as output:
BLABLABLA :
TEXTEXOUA..:
TEXT :
MORE TEXT..:
I also solved the first part of the problem (when its over 10 characters), only the second one gives me trouble.
AMOUNT=definition here, just simplyfying so not including it
for (( i=1; i<="$AMOUNT"; i++ )); do
STRING=definition here, just simplyfying so not including it
DOTS="..:"
STRING_LENGTH=`echo -n "$STRING" | wc -c`
if [ "$STRING_LENGTH" -gt 10 ]
then
#Takes
STRING=`echo -n "${STRING:0:10}"$DOTS`
else
#now i dont know what to do here, how can i take my current $STRING
#and add spaces " " until we reach 10 characters. Any ideas?
fi
Bash provides a simple way to get the length of a string stored in a variable: ${#STRING}
STRING="definition here, just simplyfying so not including it"
if [ ${#STRING} -gt 10 ]; then
STR12="${STRING:0:10}.."
else
STR12="$STRING " # 12 spaces here
STR12="${STR12:0:12}"
fi
echo "$STR12:"
The expected output you posted doesn't match the requirements in the question. I tried to follow the requirements and ignored the sample expected output and the code you posted.
Use printf:
PADDED_STRING=$(printf %-10s $STRING)

bash - difference between two text files

Let's say there are two text files and I need to check if they are different.
If they are, I need to make some changes to them and display information on the terminal.
Will something like this work?
diff file1.txt file2.txt > difference.txt
if [ -s difference.txt ]
then
.....
else
.....
fi
I also tried to find some other ways of writing this in bash, and I've found this code :
DIFF_OUTPUT="$(diff new.html old.html)"
if [ "0" != "${#DIFF_OUTPUT}" ]; then
But I can't quite understand it.
I guess in the first line we create a variable DIFF_OUTPUT which works just like difference.txt in my code?
Then there's
${#DIFF_OUTPUT}
which I don't understand at all. What's going on here?
I apologise if my questions are very basic, but I couldn't find an answer anywhere else.
diff has an exit status of 1 if the files are different.
diff file1.txt file2.txt > difference.txt
status=$?
case $status in
0) echo "Files are the same"
# more code here
;;
1) echo "Files are different"
# more code here
;;
*) echo "Error occurred: $status"
# more code here
;;
esac
If you aren't concerned with errors, then just check for a zero-vs-non-zero condition:
if diff file1.txt file2.txt > difference.txt; then
# exit status was 0, files are the same
else
# exit status was > 0, files are different or an error occurred
fi
The first line sets a variable DIFF_OUTPUT as the output/terminal result of the command diff new.html old.html.
This is called command substitution. You can encapsulate an expression inline by using $(). Think of it as copying the expression into a terminal and running it and then pasting the result straight back into your code.
So, DIFF_OUTPUT now contains the output of the diff of the two files. If the files are identical, then diff will output nothing, thus the variable DIFF_OUTPUT will be assigned an empty string.
${#variable} returns the length of a variable in bash. Thus, if there was no difference between the files, the variable (DIFF_OUTPUT) will be an empty string - which has a length of 0. Thus, ${#DIFF_OUTPUT} == "0", meaning that, if there was a difference in the files, ${#DIFF_OUTPUT} != "0" and your condition is satisfied.
DIFF_OUTPUT="$(diff new.html old.html)"
The first line saves the output of a command diff into a variable DIFF_OUTPUT.
${#DIFF_OUTPUT}
and this expression outputs the length of DIFF_OUTPUT. ${#VAR } syntax will calculate the number of characters in a variable

read multiple values from a property file using bash shell script

Would like to read multiple values from a property file using a shell script
My properties files looks something like below, the reason I added it following way was to make sure, if in future more students joins I just need to add in in the properties file without changing any thing in the shell script.
student.properties
total_student=6
student_name_1="aaaa"
student_name_2="bbbb"
student_name_3="cccc"
student_name_4="dddd"
student_name_5="eeee"
When I run below script I not getting the desired output, for reading the student names from properties file
student.sh
#!/bin/bash
. /student.properties
i=1
while [ $i -lt $total_student ]
do
{
std_Name=$student_name_$i
echo $std_Name
#****** my logic *******
} || {
echo "ERROR..."
}
i=`expr $i + 1`
done
Output is something like this
1
2
3
4
5
I understand the script is not getting anything for $student_name_ hence only $i value is getting printed.
Hence, wanted to know how to read values from the properties file.
You can do variable name interpolation with ${!foo}. If $foo is "bar", then ${!foo} gives you the value of $bar. In your code that means changing
std_Name=$student_name_$i
to
var=student_name_$i
std_Name=${!var}
Alternatively, you could store the names in an array. Then you wouldn't have to do any parsing.
student.properties
student_names=("aaaa" "bbbb" "cccc" "dddd" "eeee")
student.sh
#!/bin/bash
. /student.properties
for student_name in "${student_names[#]}"; do
...
done
You can use indirect expansion:
std_Name=student_name_$i
echo "${!std_Name}"
the expression ${!var} basically evaluates the variable twice:
first evaluation: student_name_1
second evaluation: foo
Note that this is rarely a good idea and that using an array is almost always preferred.

command working in command line but not thru script

cat test.txt
#this is comment
line 1
line 2
#this is comment at line 3
line4
script:
result=`awk '/^#.*/ { print }' test.txt `
for x in $result
do
echo x
done
expected output:
#this is comment
#this is comment at line 3
getting output:
#this
is
comment
#this
is
comment
at
line
3
but when i execute this command awk '/^#.*/ { print }' test.txt,
i get expected result.
I am putting this in loop because I need to capture each comment one at a time, not all together.
This happens because for x in $result will loop through each word in $result - that's what for is meant to do.
Try this instead:
echo "$result" | while read x; do
echo "$x"
done
read will take one line at a time, which is what you need here.
Your issue is not the awk part, but is the for part. When you do
for x in yes no maybe why not
do
echo x
done
You'll get
yes
no
maybe
why
not
That is, the list that for is looping over is automatically space-delimited.
One fix, I suppose, is to wrap the comments in quotes; then for will treat each quoted comment as a single item. legoscia's fix (using read in a while loop) seems even better to me.

Resources