bash - difference between two text files - bash

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

Related

What this symbol mean "#?" in 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=)

How can I create a Bash script that creates multiple files with text, excluding one?

I need to create Bash script that generates text files named file001.txt through file050.txt
Of those files, all should have this text inserted "This if file number xxx" (where xxx is the assigned file number), except for file007.txt, which needs to me empty.
This is what I have so far..
#!/bin/bash
touch {001..050}.txt
for f in {001..050}
do
echo This is file number > "$f.txt"
done
Not sure where to go from here. Any help would be very appreciated.
#!/bin/bash
for f in {001..050}
do
if [[ ${f} == "007" ]]
then
# creates empty file
touch "${f}.txt"
else
# creates + inserts text into file
echo "some text/file" > "${f}.txt"
fi
done
The continue statement can be used to skip an iteration of a loop and go on to the next -- though since you actually do want to take an operation on file 7 (creating it), it makes just as much sense to have a conditional:
for (( i=1; i<50; i++ )); do
printf -v filename '%03d.txt' "$i"
if (( i == 7 )); then
# create file if it doesn't exist, truncate if it does
>"$filename"
else
echo "This is file number $i" >"$filename"
fi
done
A few words about the specific implementation decisions here:
Using touch file is much slower than > file (since it starts an external command), and doesn't truncate (so if the file already exists it will retain its contents); your textual description of the problem indicates that you want 007.txt to be empty, making truncation appropriate.
Using a C-style for loop, ie. for ((i=0; i<50; i++)), means you can use a variable for the maximum number; ie. for ((i=0; i<max; i++)). You can't do {001..$max}, by contrast. However, this does need meaning to add zero-padding in a separate step -- hence the printf.
Of course, you can costumize the files' name and the text, the key thing is the ${i}. I tried to be clear, but let us know if you don't understand something.
#!/bin/bash
# Looping through 001 to 050
for i in {001..050}
do
if [ ${i} == 007 ]
then
# Create an empty file if the "i" is 007
echo > "file${i}.txt"
else
# Else create a file ("file012.txt" for example)
# with the text "This is file number 012"
echo "This is file number ${i}" > "file${i}.txt"
fi
done

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.

change a single word in a file with Bash

i try to write a very simple bash file that allow my to open and modify n times a file.java .
The modification i want is only a change in a single (or two) row of a single number.
I try to do this with the follow code:
#!/bin/bash
# commento
touch ic.java
touch input
n=0
for n in "1" "2" "3" "4.5"
do
echo 'import java.io.*;'>ic.java
echo 'import java.util.*;'>>ic.java
echo ' '>>ic.java
echo 'class INITIAL_CONDITION_NORMAL {'>>ic.java
echo 'public static void main (String args[]) {'>>ic.java
echo "$n">>ic.java
n=$(($n+1))
echo '....'>>ic.java
done
java ic.java
as you see i must write all the file and, when i like to change the number, put the "$n"
and n=$(($n+1)) in the row then go on until the end of the file and lounch it (java ic.java).
I know i can use something like:
sed -i 'm-th_row/old/new/' ic.java
but if i want to do this recursively (100 times) whit every time a different new value (as in the example) how can i do that?
Thanks a lot for Your help !
As long as new contains no / (slash) character, or any other special character that would confuse sed, this is the sort of pattern you need.
for n in "1" "2" "3" "4.5"
sed -i "m-th_row/old/$n/" ic.java
done
Of course, that snippet would just modify the same file repeatedly, which probably wouldn't be helpful, but you get the idea.

fetching files in a loop as an input to a script

I have a bunch of files and a script which I run on them. That script takes 2 files as an input and all files are in this format: a.txt1 a.txt2
Now the script I use is like this: foo.sh a.txt1 a.txt2
I have to run this script run on 250 pairs (eg. a1.txt1 a1.txt2 to a250.txt1 a250.txt2)
I am doing this manually by entering file names. I was wondering is there any way to automate this process. All these pairs are in same folder, is there any way to loop the process on all pairs?
I hope I made it clear.
Thank you.
To be specific, these are some sample file names:
T39_C.txt2
T39_D.txt1
T39_D.txt2
T40_A.txt1
T40_A.txt2
T40_B.txt1
T40_B.txt2
T40_C.txt1
T40_C.txt2
T40_D.txt1
T40_D.txt2
unmatched.txt1
unmatched.txt2
WT11_A.txt1
WT11_A.txt2
WT11_B.txt1
WT11_B.txt2
WT11_C.txt1
Assuming all files are in pairs (ie, <something>.txt1 and <something>.txt2 then you can do something line this:
1. #!/bin/bash
2.
3. for txt1 in *.txt1; do
4. txt2="${txt1%1}2"
5. # work on $txt1 and $txt2
6. done
In line 3, we use a shell glob to grab all files ending with .txt1. Line 4, we use a substitution to remove the final 1 and replace it with a 2. And the real work is done in line 5.
#FOR EACH FILE IN THE CURRENT DIRECTORY EXCEPT FOR THE FILES WITH .txt2*
for i in ls | sort | grep -v .txt2
do
*#THE FIRST .txt1 file is $i*
first="$i"
*#THE SECOND IS THE SAME EXCEPT WITH .txt2 SO WE REPLACE THE STRING*
second=`echo "$i" | sed 's/.txt1/.txt2/g'`
#WE MAKE THE ASSUMPTION FOO.SH WILL ERROR OUT IF NOT PASSED TWO PARAMETERS
if !(bash foo.sh $first $second); then
{
echo "Problem running against $first $second"
}
else
{
echo "Ran against $first $second"
}
fi
done

Resources