EN VI

Linux - Map custom and arbitrary functions with sed or awk?

2024-03-12 23:00:12
How to Linux - Map custom and arbitrary functions with sed or awk

I'm trying to use commands sed or awk, together with arbitrary functions (or commands) defined, for example, as Bash scripts. For example, the following Bash script prepend_largest_prime.sh accepts a string as its first argument, and outputs the larger prime number below N, where N is the length of the string:

#!/bin/bash

is_prime() {
# Returns 1 if a number is prime, 0 if not
    i=2
    if [ $1 -lt 2 ]
    then
        return 0
    fi
    
    max=`echo "sqrt($1)" | bc`
    while [ $i -le $max ]
    do
        if [ `expr $1 % $i` -eq 0 ]
        then
            return 0
        fi
        i=`expr $i + 1`
    done
    
    return 1
}

largest_prime_under() {
    counter=$1
    is_prime $counter 
    counter_primeQ=$?

    if [[ $counter_primeQ = 1 ]]; then
        echo $counter
        exit 1
    fi
    counter=$[ $counter - 1 ]

    while [[ $counter -gt 0 ]] ; do
        is_prime $counter
        counter_primeQ=$?
        if [[ $counter_primeQ = 1 ]]; then
            echo "$counter"
            exit 1
        fi
        counter=$[ $counter - 1 ]
    done
}

string_length() {
    str="$1"
    n=${#str}
    echo "$n"
}

string_length_num=$(string_length "$1")
largest_prime_under_string=$(largest_prime_under $string_length_num)

echo $largest_prime_under_string $1

An example call of this script could be:

$ ./prepend_largest_prime.sh 'The quick brown fox jumps over the lazy dog.'
43 The quick brown fox jumps over the lazy dog.

Here the passed string has 44 characters, and the larger prime number below 44 is 43.

This is all I have managed to do. It still doesn't work and seems too complicated for the task at hand:

cat input_file.txt
The majestic mountains towered over the quaint little beautiful village below.
The serene garden is colorful.
The old oak trees stood tall, their branches reaching towards the sky.
$ sed "s|.*|\"\$(\./prepend_largest_prime.sh '&')\"|" input_file.txt
"$(./prepend_largest_prime.sh 'The majestic mountains towered over the quaint little beautiful village below.')"
"$(./prepend_largest_prime.sh 'The serene garden is colorful.')"
"$(./prepend_largest_prime.sh 'The old oak trees stood tall, their branches reaching towards the sky.')"

For this example, the output of the command I'm looking for should be

73 The majestic mountains towered over the quaint little beautiful village below.
29 The serene garden is colorful.
67 The old oak trees stood tall, their branches reaching towards the sky.

How can I achieve this? Thanks!

Solution:

Preliminary: All of the following assumes that the data are presented one item per line, without embedded newlines, and that all characters between newlines are significant.

I'm trying to use commands sed or awk, together with arbitrary functions (or commands) defined, for example, as Bash scripts.

That's awkward, no pun intended. What you describe is better suited to the shell, as was described in comments. Alternatively, xargs is designed expressly for converting input into arguments of commands, as you want to do:

xargs -d '\n' -n1 ./prepend_largest_prime.sh < input.txt

If you must drive it via awk or sed then you do have options. POSIX does not define a means by which sed can execute external commands, but if you can rely on extensions provided by GNU sed then its e command could serve. You would edit the input line into the command you want to execute, then execute it with e:

sed "s,^\(.*\)\$,./prepend_largest_prime.sh '\1',; e" input.txt

There are other ways to work the details, but that particular approach uses an s command to wrap the input line in single quotes and prepend the name of the command to execute. Note well that it will break if the input contains single quotes, though you could accommodate that with a bit more effort.

The awk version is more standard and a bit easier to read, using its system() function, but the idea is largely the same. Edit each input line into the shell command you want to execute, and pass the result to system():

awk '{ system("./doit.sh '"'"'" $0 "'"'"'") }' < input.txt

Like with the sed-based approach, you would need to do more work to accommodate inputs containing single-quote characters.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login