在 awk 中使用使用者定義的 bash 函數

在 awk 中使用使用者定義的 bash 函數

是否可以在 AWK 中使用 bash 函數?

範例檔案(字串、int、int、int)

Mike 247808 247809 247810

嘗試將值從十進制轉換為十六進制。

在 shell 腳本中或 shell 腳本中定義的函數.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

編輯:

作為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

您可以從 awk 呼叫 bash 並使用其輸出。從性能角度來看,如果這種情況發生得太頻繁,顯然是危險的。引用手冊頁:

command | getline [var]

運行命令將輸出傳輸到 $0 或 var,

命令將是一個 bash 腳本,其中包含函數定義並執行該函數。

答案3

從十進制轉換為十六進位awk本身就可以很好地完成。你可以定義一個awk函數來做到這一點:

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

現在要回答一般情況下的問題,為了awk執行bash函數,您需要awk執行一個bashshell,它bash來解釋該函數的定義,並呼叫該函數,並將提取的值awk作為參數傳遞。

不是微不足道的。

bash支援透過環境導出函數,因此它可以在 的後續呼叫中使用bash,因此這是將函數的定義傳遞給bash呼叫者的一種方法awk

export -f d2h

awk執行命令(此處)的唯一方法bash是使用其system("cmd"),print... | "cmd""cmd" | getline。在所有情況下,awk都會執行 shell 來解釋該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)(這會使cmd標準輸出保持不變)。

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

在 awk 中使用使用者定義的 bash 函數

免責聲明:我意識到這不是OP想要做的,但谷歌會引導像我這樣的其他人找到這個答案。

情況

您有一個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

相關內容