Score:14

Delete a file if multiple conditions are met

ma flag

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!

Score:9
cn flag
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"
}
Justin Washek avatar
ma flag
Awesome, this did the trick, thank you so much!
Score:3
ph flag

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.

Score:1
cn flag

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; }
Score:1
cn flag

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"
minnmass avatar
in flag
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.
rexkogitans avatar
cn flag
@minnmass Thank you, I did not get the question right. I fixed it.
Score:-3
in flag

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

hr flag
`<` is stdin redirection inside *single* test brackets `[ ... ]`; inside `[[ ... ]]` as used by the OP it's a lexicographical comparison operator. See for example [Conditional Constructs](https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs)
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.