将输出管道输出到具有多个输入的 bash 函数
Piping output to a bash function with multiple inputs
这是我正在尝试做的事情:我想使用 bash 测量两个字符串之间的 Levensthein 距离。我找到了 LD here.
的实现
现在,假设我有一些像这样的玩具数据:
1 The brown fox jumped The green fox jumped
0 The red fox jumped The green fox jumped
1 The gray fox jumped The green fox jumped
假设这存储在 data.test
.
然后我通过一个简单的 awk
命令来过滤掉以 1
开头的行,如下所示:
awk -F '\t' '{if (>0) print ,t,}' data.test
这个简单命令的第一个输出将是:
The brown fox jumped The green fox jumped
我现在想测量这两个句子之间的 Levensthein 距离,方法是将此输出直接输送到此函数(从上面 link 中提取):
function levenshtein {
if (( $# != 2 )); then
echo "Usage: [=14=] word1 word2" >&2
elif (( ${#1} < ${#2} )); then
levenshtein "" ""
else
local str1len=${#1}
local str2len=${#2}
local d
for i in $( seq 0 $(( (str1len+1)*(str2len+1) )) ); do
d[i]=0
done
for i in $( seq 0 $str1len ); do
d[i+0*str1len]=$i
done
for j in $( seq 0 $str2len ); do
d[0+j*(str1len+1)]=$j
done
for j in $( seq 1 $str2len ); do
for i in $( seq 1 $str1len ); do
[ "${1:i-1:1}" = "${2:j-1:1}" ] && local cost=0 || local cost=1
del=$(( d[(i-1)+str1len*j]+1 ))
ins=$(( d[i+str1len*(j-1)]+1 ))
alt=$(( d[(i-1)+str1len*(j-1)]+cost ))
d[i+str1len*j]=$( echo -e "$del\n$ins\n$alt" | sort -n | head -1 )
done
done
echo ${d[str1len+str1len*(str2len)]}
fi
}
我知道你可以做到这一点,但我被两个需要传递的参数以及我正在传递序列的事实所困扰。
我尝试过使用各种版本的 this 建议,这些建议提倡这样获取输入:
function levenshtein {
# Grab input.
declare input1=${1:-$(</dev/stdin)};
declare input2=${2:-$(</dev/stdin)};
.
.
.
}
这是我无法完成的部分。
如果在使用export -f levenshtein
调用awk之前导出bash中的Levenshtein函数,则可以轻松地逐行调用awk中的函数:
awk -F '\t' '>0 {system("levenshtein \"""\" \"""\"")}'
.
你根本不需要awk
:
while IFS=$'\t' read num first second; do
[[ $num -gt 0 ]] || continue
levenshtein "$first" "$second"
done < data.txt
(是的,awk
在处理大文件时比 bash
更快,但是如果您首先在 bash
中实现 Levenshtein 算法,速度可能不是一个问题。)
顺便说一句,通过使用以 "tuples" 作为键的关联数组,不需要太多索引算法的更简单(尽管经过最低限度测试)的实现。
levenshtein () {
if (( ${#1} < ${#2} )); then
levenshtein "" ""
return
fi
local str1len str2len cost m a b i j
local -A d
str1len=${#1}
str2len=${#2}
for ((i=0;i<=strlen1;i++)); do
d[$i,0]=0
done
for ((j=0;j<=strlen2;j++)); do
d[0,$j]=0
done
for ((j=1; j<=str2len; j++)); do
for ((i=1; i<=str1len; i++)); do
a=${1:i-1:1}
b=${2:j-1:1}
[ "$a" = "$b" ] && cost=0 || cost=1
del=$(( $d[$((i-1)),$j] + 1 ))
ins=$(( $d[$i,$((j-1))] + 1 ))
alt=$(( $d[$((i-1)),$((j-1))] + cost ))
# Compute the min without forking
m=$del; ((ins < m)) && m=$ins; ((alt < m)) && m=$alt
d[$i,$j]=$m
done
done
echo ${d[$str1len,$str2len]}
}
我赞成 Chepner 的回答,但如果由于某种原因你发现自己陷入了一个你真正需要解决这个问题的地方,那也不难。
# Awk script refactored slightly for aesthetics
pair=$(awk -F '\t' '>0 {print "\t" }' data.test)
levenshtein "${pair%$'\t*'}" "${pair#$'*\t'}"
稍微解压一下;
levenshtein
的两个参数用双引号引起来。
- 每个参数都包含一个参数替换;
${variable%pattern}
产生 variable
的值,并删除任何匹配 pattern
的后缀
${variable#pattern}
产生 variable
的值,删除任何匹配 pattern
的前缀
- 这些都匹配最短的
pattern
。如果您有一个包含多个字段的字符串,您可能需要 ##
或 %%
变体,它们 trim 最长适用 pattern
从值的前面或后面分别.
$'\t'
是一个 C-style 字符串,其中包含制表符
pattern
还在选项卡前面或后面包含一个 *
以删除选项卡前后的所有内容,根据需要从 [= 获取第一个或第二个值48=] 字符串.
这是我正在尝试做的事情:我想使用 bash 测量两个字符串之间的 Levensthein 距离。我找到了 LD here.
的实现现在,假设我有一些像这样的玩具数据:
1 The brown fox jumped The green fox jumped
0 The red fox jumped The green fox jumped
1 The gray fox jumped The green fox jumped
假设这存储在 data.test
.
然后我通过一个简单的 awk
命令来过滤掉以 1
开头的行,如下所示:
awk -F '\t' '{if (>0) print ,t,}' data.test
这个简单命令的第一个输出将是:
The brown fox jumped The green fox jumped
我现在想测量这两个句子之间的 Levensthein 距离,方法是将此输出直接输送到此函数(从上面 link 中提取):
function levenshtein {
if (( $# != 2 )); then
echo "Usage: [=14=] word1 word2" >&2
elif (( ${#1} < ${#2} )); then
levenshtein "" ""
else
local str1len=${#1}
local str2len=${#2}
local d
for i in $( seq 0 $(( (str1len+1)*(str2len+1) )) ); do
d[i]=0
done
for i in $( seq 0 $str1len ); do
d[i+0*str1len]=$i
done
for j in $( seq 0 $str2len ); do
d[0+j*(str1len+1)]=$j
done
for j in $( seq 1 $str2len ); do
for i in $( seq 1 $str1len ); do
[ "${1:i-1:1}" = "${2:j-1:1}" ] && local cost=0 || local cost=1
del=$(( d[(i-1)+str1len*j]+1 ))
ins=$(( d[i+str1len*(j-1)]+1 ))
alt=$(( d[(i-1)+str1len*(j-1)]+cost ))
d[i+str1len*j]=$( echo -e "$del\n$ins\n$alt" | sort -n | head -1 )
done
done
echo ${d[str1len+str1len*(str2len)]}
fi
}
我知道你可以做到这一点,但我被两个需要传递的参数以及我正在传递序列的事实所困扰。
我尝试过使用各种版本的 this 建议,这些建议提倡这样获取输入:
function levenshtein {
# Grab input.
declare input1=${1:-$(</dev/stdin)};
declare input2=${2:-$(</dev/stdin)};
.
.
.
}
这是我无法完成的部分。
如果在使用export -f levenshtein
调用awk之前导出bash中的Levenshtein函数,则可以轻松地逐行调用awk中的函数:
awk -F '\t' '>0 {system("levenshtein \"""\" \"""\"")}'
.
你根本不需要awk
:
while IFS=$'\t' read num first second; do
[[ $num -gt 0 ]] || continue
levenshtein "$first" "$second"
done < data.txt
(是的,awk
在处理大文件时比 bash
更快,但是如果您首先在 bash
中实现 Levenshtein 算法,速度可能不是一个问题。)
顺便说一句,通过使用以 "tuples" 作为键的关联数组,不需要太多索引算法的更简单(尽管经过最低限度测试)的实现。
levenshtein () {
if (( ${#1} < ${#2} )); then
levenshtein "" ""
return
fi
local str1len str2len cost m a b i j
local -A d
str1len=${#1}
str2len=${#2}
for ((i=0;i<=strlen1;i++)); do
d[$i,0]=0
done
for ((j=0;j<=strlen2;j++)); do
d[0,$j]=0
done
for ((j=1; j<=str2len; j++)); do
for ((i=1; i<=str1len; i++)); do
a=${1:i-1:1}
b=${2:j-1:1}
[ "$a" = "$b" ] && cost=0 || cost=1
del=$(( $d[$((i-1)),$j] + 1 ))
ins=$(( $d[$i,$((j-1))] + 1 ))
alt=$(( $d[$((i-1)),$((j-1))] + cost ))
# Compute the min without forking
m=$del; ((ins < m)) && m=$ins; ((alt < m)) && m=$alt
d[$i,$j]=$m
done
done
echo ${d[$str1len,$str2len]}
}
我赞成 Chepner 的回答,但如果由于某种原因你发现自己陷入了一个你真正需要解决这个问题的地方,那也不难。
# Awk script refactored slightly for aesthetics
pair=$(awk -F '\t' '>0 {print "\t" }' data.test)
levenshtein "${pair%$'\t*'}" "${pair#$'*\t'}"
稍微解压一下;
levenshtein
的两个参数用双引号引起来。- 每个参数都包含一个参数替换;
${variable%pattern}
产生variable
的值,并删除任何匹配pattern
的后缀${variable#pattern}
产生variable
的值,删除任何匹配pattern
的前缀- 这些都匹配最短的
pattern
。如果您有一个包含多个字段的字符串,您可能需要##
或%%
变体,它们 trim 最长适用pattern
从值的前面或后面分别.
$'\t'
是一个 C-style 字符串,其中包含制表符pattern
还在选项卡前面或后面包含一个*
以删除选项卡前后的所有内容,根据需要从 [= 获取第一个或第二个值48=] 字符串.