3
\$\begingroup\$

I've made this script to automate blocking some deviant hosts on my router, and was curious if there's anything much else that can be done to make it quicker and more efficient.

Presently I'm restricted to using packages available on the Entware repository and the latest Busybox/ash shell environment. So, for example, I can't use commands like sort -S 25% --parallel=4 -u adblock.sources (though awk/gawk is faster and more memory efficient anyway).

update_the_blacklist.sh

curl -GOs https://raw.githubusercontent.com/T145/packages/master/net/adblock/files/adblock.sources

for key in $(jq -r 'keys[]' adblock.sources); do
        case $key in
        gaming | osid_basic )
                # Ignore these lists
                ;;
        * )
                url=$(jq -r ".$key.url" adblock.sources)
                rule=$(jq -r ".$key.rule" adblock.sources)

                case $url in
                *.tar.gz )
                        curl -s $url | \
                        tar -xOzf - | \
                        gawk "$rule" | \
                        sed "s/\r//g" | \
                        sed 's/^/0.0.0.0 /' >> the_blacklist.temp.txt
                        ;;
                * )
                        curl -s $url | \
                        gawk "$rule" | \
                        sed "s/\r//g" | \
                        sed 's/^/0.0.0.0 /' >> the_blacklist.temp.txt
                esac

                unset url
                unset rule
        esac
done

# Filter duplicate hosts
gawk '!a[$0]++' the_blacklist.temp.txt > the_blacklist.txt

rm the_blacklist.temp.txt
rm adblock.sources
\$\endgroup\$
2
\$\begingroup\$

We're missing a shebang line. The question is tagged , but that seems overkill, since there's nothing here that's not standard POSIX shell.

#!/bin/sh

We append to the_blacklist.temp.txt, but don't ensure that it starts off empty, or that it's even writeable. It's best to create temporary files using mktemp:

tmpfile=mktemp || exit $?
trap "rm $tmpfile" EXIT

Also, instead of directing final output to the_blacklist.txt, it would be more flexible to write to standard output, and let the user choose where to send it (perhaps pipe into another filter).

But there seems to be no reason to use a file here - just pipe the for loop directly into awk (or into sort -u - that may be a better choice if you're memory-constrained, as it will perform an external merge-sort if it needs to).

The duplicated sed pipe could be replaced with a single sed program of two commands:

for
    …
done |
    sed -e 's/\r//g' -e 's/^/0.0.0.0 /' | gawk '!a[$0]++'

There's a security issue here:

                    gawk "$rule" | \

We are executing downloaded content in awk. Because we can't be sure that the adblock.sources URL cannot be replaced with a more harmful version, we should run awk in its constrained "sandbox" mode, and use the -- separator so we don't interpret the script as arguments.

                    gawk --sandbox -- "$rule" | \

These changes give me

#!/bin/sh

set -eu

sources=$(mktemp)
trap 'rm "$sources"' EXIT

curl -s -o "$sources" https://raw.githubusercontent.com/T145/packages/master/net/adblock/files/adblock.sources

for key in $(jq -r 'keys[]' "$sources")
do
    case $key in
        gaming | osid_basic )
        # Ignore these lists
        ;;
        * )
            url=$(jq -r ".$key.url" "$sources")
            rule=$(jq -r ".$key.rule" "$sources")

            curl -s "$url" |
                case $url in
                    *.tar.gz) tar -xOzf - ;;
                    *) cat ;;
                esac |
                gawk --sandbox -- "$rule"
    esac
done |
    sed -e 's/\r//g' -e 's/^/0.0.0.0 /' | sort -u

Somebody more expert with jq could probably avoid the remaining temporary file by making a single pass over the retrieved data.

\$\endgroup\$
0

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.