Verwenden einer benutzerdefinierten Bash-Funktion in awk

Verwenden einer benutzerdefinierten Bash-Funktion in awk

Ist es irgendwie möglich, die Bash-Funktion in AWK zu verwenden?

Beispieldatei (String, Int, Int, Int)

Mike 247808 247809 247810

Es wird versucht, Werte von Dezimal in Hexadezimal umzuwandeln.

Funktion, die entweder in .bashrcoder im Shell-Skript definiert ist.

$ awk '{print $1 ; d2h($2)}' file

awk: calling undefined function d2h
 input record number 1, file file
 source line number 1

Antwort1

Versuchen Sie, die folgende Funktion zu verwenden system():

awk '{printf("%s ",$1); system("d2h " $2)}' file

In Ihrem Fall wird die Ausgabe dieses Befehls systemaufgerufen und an die Ausgabe angehängt:d2h 247808printf

Mike 3C800

BEARBEITEN:

As systemverwendet shanstelle von bashIch kann keinen Weg finden, darauf zuzugreifen .bashrc. Sie können aber weiterhin Funktionen aus Ihrem aktuellen Bash-Skript verwenden:

#!/bin/bash
d2h() {
    # do some cool conversion here
    echo "$1" # or just output the first parameter
}
export -f d2h
awk '{printf("%s ",$1); system("bash -c '\''d2h "$2"'\''")}' file

Hinweis: -fwird verwendet, umExportieren einer Funktionund nicht eine Variable.

BEARBEITEN 2:

Ich weiß nicht, warum, aber das funktioniert auf meinem Ubuntu 16.04 nicht. Das ist seltsam, denn auf Ubuntu 14.04 hat es früher funktioniert.

Antwort2

Sie können bash von awk aus aufrufen und dessen Ausgabe verwenden. Das ist aus Leistungssicht natürlich gefährlich, wenn es zu oft vorkommt. Zitat aus der Manpage:

command | getline [var]

Führen Sie den Befehl aus und leiten Sie die Ausgabe entweder an $0 oder var weiter.

Befehl wäre ein Bash-Skript, das die Funktionsdefinition enthält und die Funktion ausführt.

Antwort3

Die Konvertierung von Dezimalzahlen in Hexadezimalzahlen kann man awkganz einfach selbst durchführen. Und man kann eineawkFunktion, um dies zu tun:

function d2h(d) {
  return sprintf("%x", d)
}

Um die Frage nun allgemein zu beantworten: Zum awkAusführen bashvon Funktionen müssten Sie eine Shell awkausführen , die die Definition dieser Funktion interpretiert und diese Funktion mit dem extrahierten Wert aufruft, der als Argumente übergeben wird.bashbashawk

Nicht trivial.

bashunterstützt das Exportieren von Funktionen über die Umgebung, so dass es in nachfolgenden Aufrufen von verfügbar ist bash. Dies ist also eine Möglichkeit, die Definition der Funktion an das bashvon aufgerufene Element zu übergeben awk:

export -f d2h

Die einzigen Möglichkeiten, einen Befehl ( hier) awkauszuführen, sind mit seinem , oder oder . In allen Fällen wird eine Shell ausgeführt, um das zu interpretieren , aber es wird sein , nicht . Sie müssen also eine Befehlszeile für konstruieren, die ein Aufruf ist, der eine Befehlszeile interpretiert, um die Funktion aufzurufen, also müssen Sie bei der Verwendung von Anführungszeichen vorsichtig sein:bashsystem("cmd")print... | "cmd""cmd" | getlineawkcmdshbashshbashbash

export -f d2h
<file awk -v q="'" '
  function shquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  {print $1; system("exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2))}'

Wenn Sie die Ausgabe der Funktion zurück in erhalten möchten awk, müssen Sie sie über eine Pipe zurück übertragen. Dazu verwenden Sie cmd | getlineanstelle von system(cmd)(wobei cmddie Standardausgabe von unberührt bleibt).

cmd | getline lineShopseine Linie(genau genommenein Datensatz, Datensätze sind standardmäßig Zeilen), sodass Sie, um die gesamte Ausgabe in den Fällen zu erhalten, in denen sie aus mehreren Zeilen besteht, eine Schleife wie die folgende benötigen:

awk '...
  cmd = "exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2)
  output = ""
  while ((cmd | getline line) > 0) {
    output = output line RS
  }
  sub(RS "$", "", output) # remove the last newline
  ...'

Das bedeutet, dass für jeden Aufruf der Funktion eins shund eins ausgeführt wird bash, was ziemlich ineffizient ist. Das wäre am Ende sogar noch deutlich ineffizienter, als bashdas Lesen und Aufteilen mit einem while read loop: durchzuführen.

(unset -v IFS; while read -r a b rest; do
  printf '%s\n' "$a"
  d2h "$b"
 done < file)

Beachten Sie auch, dass seit Shellshock bashjetzt Exportfunktionen in Umgebungsvariablen mit Namen wie verwendet werden BASH_FUNC_d2h%%. Einige shImplementierungen, einschließlich mkshund neuere Versionen vondash entfernendiese Umgebungsvariablen aus der Umgebung:

$ env 'foo%%=bar' dash -c 'printenv foo%%'
$ env 'foo%%=bar' mksh -c 'printenv foo%%'
$ env 'foo%%=bar' zsh  -c 'printenv foo%%'
bar
$ env 'foo%%=bar' bash -c 'printenv foo%%'
bar

Anstatt sich also auf die schwache Funktion zum Exportieren von Funktionen zu verlassen, können Sie die Funktionsdefinition auch auf andere Weise übergeben. Dies könnte über eine Umgebungsvariable mit einem üblichen Namen erfolgen:

BASH_FUNCTIONS=$(typeset -f d2h) awk '
   ...
   cmd = "exec bash -c '\''eval \"$BASH_FUNCTIONS\";" \
         "d2h \"$1\"'\'' bash " shquote($2)
   ...'

Antwort4

Verwenden einer benutzerdefinierten Bash-Funktion in awk

Haftungsausschluss:Mir ist klar, dass dies nicht das ist, was der OP versucht, aber Google wird andere wie mich zu dieser Antwort führen.

Situation

Sie haben ein bashSkript, das mit Funktionen organisiert ist (weil Sie weder sich selbst noch [die meisten] Kollegen hassen) und mindestens eine dieser Funktionen muss eine andere von innen heraus aufrufen awk.

Lösung

Skript

#!/bin/env bash

# The main function - it's a sound pattern even in BASH
main(){
    # In the awk command I do some tricky things with single quotes. Count carefully...
    # The first $0 is outside the single quotes so it is the name of the current bash script.
    # The second $0 is inside the single quotes so it is awk's current line of input.
    awk '{printf("%s. ", ++c); system("'$0' --do"); print $0}'<<-PRETEND_THIS_IS_AN_INPUT_STREAM
        and
        and
        well
    PRETEND_THIS_IS_AN_INPUT_STREAM
}

# functionized to keep things DRY
doit(){
    echo -n "doin' it "
}


# check for a command switch and call different functionality if it is found
if [[ $# -eq 1 && $1 == "--do" ]];
then
        doit
else
        main
fi

Ausgabe

$ ./example.sh
1. doin' it and
2. doin' it and
3. doin' it well

verwandte Informationen