Использование пользовательской функции bash внутри awk

Использование пользовательской функции bash внутри awk

Можно ли как-то использовать функцию bash внутри AWK?

Пример файла (строка, целое, целое, целое)

Mike 247808 247809 247810

Попытка преобразовать значения из десятичных в шестнадцатеричные.

Функция определена либо в .bashrcскрипте оболочки, либо в нем.

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

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

решение1

Попробуйте использовать system()функцию:

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

В вашем случае systemвызовем d2h 247808и затем добавим вывод этой команды к printfвыводу:

Mike 3C800

РЕДАКТИРОВАТЬ:

As systemиспользует shвместо bashЯ не могу найти способ получить доступ .bashrc. Но вы все равно можете использовать функции из вашего текущего скрипта bash:

#!/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

Примечание: -fиспользуется дляэкспортировать функциюа не переменная.

ПРАВКА 2:

Не знаю почему, но это не работает на моем Ubuntu 16.04. Это странно, потому что раньше это работало на Ubuntu 14.04.

решение2

Вы можете вызвать bash из awk и использовать его вывод. Это, очевидно, опасно с точки зрения производительности, если это происходит слишком часто. Цитата из man-страницы:

command | getline [var]

Выполнить команду, перенаправив вывод в $0 или var,

команда будет представлять собой bash-скрипт, содержащий определение функции и выполняющий эту функцию.

решение3

Перевод из десятичной системы в шестнадцатеричную — это то, что awkможет сделать само. И вы можете определитьawkФункция, которая это делает:

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

Теперь, чтобы ответить на вопрос в общем случае, для awkзапуска bashфункций вам нужно будет awkзапустить bashоболочку, которая bashинтерпретирует определение этой функции, и вызвать эту функцию, передав извлеченное значение awkв качестве аргументов.

Не тривиально.

bashподдерживает экспорт функций через среду, поэтому он доступен в последующих вызовах bash, так что это один из способов передать определение функции bashвызываемому awk:

export -f d2h

Единственные способы awkвыполнить команду ( bashздесь) — с помощью system("cmd"), или print... | "cmd"или "cmd" | getline. Во всех случаях awkзапускает оболочку для интерпретации этого cmd, но это будет sh, а не bash. Поэтому вам нужно создать командную строку для shвызова bash, которая интерпретирует bashкомандную строку для вызова функции, поэтому вам нужно быть осторожным с кавычками:

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))}'

Если вы хотите вернуть вывод функции обратно в awk, вам нужно будет передать его обратно через канал. Для этого вы должны использовать cmd | getlineвместо system(cmd)(что оставляет cmdstdout нетронутым).

cmd | getline lineмагазиныодна линия(строго говоряодна запись, записи по умолчанию являются строками), поэтому для получения всего вывода в случаях, когда он состоит из нескольких строк, вам понадобится такой цикл:

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
  ...'

Это означает запуск одного shи одного bashдля каждого вызова функции, поэтому это будет довольно неэффективно. Это в конечном итоге будет даже значительно более неэффективно, чем делать bashчтение и разделение с помощью while read loop:

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

Также обратите внимание, что начиная с shellshock, bashтеперь экспортируются функции в переменные окружения, которые называются как BASH_FUNC_d2h%%. Некоторые shреализации, включая mkshи более новые версииdash удалятьэти переменные среды из среды:

$ 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

Поэтому вместо того, чтобы полагаться на шаткую функцию экспорта функций, вы можете передать определение функции другим способом. Это может быть сделано через переменную окружения с обычным именем:

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

решение4

Использование пользовательской функции bash внутри awk

Отказ от ответственности:Я понимаю, что это не то, что пытается сделать автор поста, но Google поможет другим, таким как я, найти этот ответ.

Ситуация

У вас есть bashскрипт, организованный с помощью функций (потому что вы не ненавидите себя или [большинство] коллег), и по крайней мере одна из этих функций должна вызывать другую изнутри awk.

Решение

Сценарий

#!/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

Выход

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

Связанный контент