12

What I need

I have an existing script that pulls port information for domains and stores it into a text file called portscan.txt. Example:

portscan.txt file:

somedomain.com:80
somedomain.com:443

I want to delete the information only if certain conditions are met. These conditions include:

  • The file with the domains should have 2 or less lines
  • The ports should only be 80 or 443 (i.e., I don't want to delete the file if 8080, 8443, or any other port exists in the file).

Note: So basically, the example file provided above should be deleted, but I do not want to delete the file if there are 2 lines, but the ports are 8080 or 8443 (or any other port for that matter) Example like so:

somedomain.com:8443
somedomain.com:443

This should not be deleted.

What I tried

I attempted scripting this out and here's what I have:

#!/bin/bash

lines=$(cat portscan.txt | wc -l)
ports=$(cat portscan.txt | grep -Pv '(^|[^0-9])(80|443)($|[^0-9])')


if [[ $lines < 3 ]] && [[ $ports != 80 ]]; then
    if [[ $ports != 443 ]]; then
        echo "I need to delete this"
    fi
else
    echo "I will NOT delete this..."
fi

This is the second rendering of the script, I attempted nested if statements because I was unable to do a condition like this:

IF portscan.txt is less than two lines AND the ports are NOT 80 OR 443

I also attempted this in a much simpler manner like so:

#!/bin/bash

lines=$(cat portscan.txt | wc -l)
ports=$(cat portscan.txt | grep -Pv '(^|[^0-9])(80|443)($|[^0-9])')


if [[ $lines < 3 ]] && (( $ports != 80||443 )); then
    echo "I need to delete this"
else
    echo "I will NOT delete this..."
fi

I have tried the (( because I read that this is better to be used with arithmetic functions -- which is what I thought I needed, but I'm not as bash savvy with conditional arguments when it should be like: "This and that or that".

Hopefully this makes sense, any help would be greatly appreciated!

8
checkfile() {
    awk '
        BEGIN {
            FS = ":"
            status = 1
            ports[80] = 1
            ports[443] = 1
        }
        NR == 3 || !($2 in ports) {status = 0; exit}
        END {exit status}
    ' "$1"
}

file=portscan.txt
checkfile "$file" || echo rm -- "$file"

That awk command will exit with status 0 if the file has a 3rd line, or if it sees a "non-standard" port.

If the function returns non-zero (the file has <= 2 lines and only "standard" ports), then the rm command is printed.

Remove echo if the results look right.


Alternately:

checkfile() {
    # if more than 2 lines, keep the file
    (( $(wc -l < "$1") > 2 )) && return 0

    # if a "non-standard" port exists, keep the file
    grep -qv -e ':80$' -e ':443$' "$1" && return 0

    # delete the file
    return 1
}

or, more tersely

checkfile() {
    (( $(wc -l < "$1") > 2 )) || grep -qv -e ':80$' -e ':443$' "$1"
}
1
  • Awesome, this did the trick, thank you so much! Nov 24 at 18:22
3

Ok, try this

[~/my_learning/lint]$ cat file.txt
somedomain.com:80
somedomain.com:443
[~/my_learning/lint]$ cat file_dont_delete.txt
somedomain.com:8443
somedomain.com:443

[~/my_learning/lint]$ for file in file.txt file_dont_delete.txt
do
num_of_lines=$(wc -l $file| xargs | awk '{print $1}')
port_scan=$(awk -F':' '{ if (($2 == "443") || ($2 == "80")) print "matched" }' $file | wc -l | xargs)
if [ $num_of_lines -le 2 ] && [ $num_of_lines -eq $port_scan ] ; then
echo "$file can be deleted"
else
echo "$file can't be deleted"
fi
done

# Output
file.txt can be deleted
file_dont_delete.txt can't be deleted

I follow the below conditions

  • number of lines 2 or less than or 2.
  • and for each line, I used awk to extract field 2 which is a port and check if it is either 80 or 443, and on any, I am printing matching.
  • Then I am counting how many matching occurred.
  • As per your description, even a single port that is not from 80 or 443 is presented, then I shouldn't delete the file.

Thank you for giving me this opportunity of writing the shell code.

1

Solution based on mapfile:

#!/bin/bash

shopt -s extglob

mapfile -tn3 a < $1
[[ ${#a[@]} -lt 3 ]] && { \
[[ ${#a[@]} -gt 0 && ${a[@]/%:@(80|443)} =~ :[0-9]+ ]] || echo rm -v -- $1; }
1

Pure bash solution, wrapped in a function and easy to read:

#!/bin/bash

possiblyDeleteFile() {
    local count=0
    local rLine port
    local otherPort=false
    while IFS= read -r -d'' rLine; do
        port="${rLine#*:}"
        ((count++))
        if [ "$port" != 80 ] && [ "$port" != 443 ]; then
            otherPort=true
        fi
    done < "$1"
    if [ $count -le 2 ] && ! $otherPort; then
        echo "Deleting file $1"
        rm -- "$1"
    fi
}

possiblyDeleteFile "$1"
2
  • I may be missing something, I don't see how this handles the "2 or less" rule - if there are 3 lines that all have port 80, count won't be incremented, so the file won't be deleted.
    – minnmass
    2 days ago
  • @minnmass Thank you, I did not get the question right. I fixed it. 2 days ago
-3

To do numeric comparisons in bash you need to use -gt, -lt, etc. The < symbol is for stdin redirection.

You can either enclose the line number comparison in (( )) like you did for the port, or use -lt See https://tldp.org/LDP/abs/html/comparison-ops.html

New contributor
Steve Folly is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • 3
    < is stdin redirection inside single test brackets [ ... ]; inside [[ ... ]] as used by the OP it's a lexicographical comparison operator. See for example Conditional Constructs Nov 24 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.