
是否可以在 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
執行一個bash
shell,它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