为什么我在 Bash 中的过滤功能不起作用?

Jon*_*mar 0 shell-script

我写了一个过滤器/选择函数,它接受一个函数和一个流作为输入。它应该产生一个新的 ( 2 4 ) 数组。然而,我的结果是什么。我怀疑它与IFS有关。

# func int -> bool
is_even () { (( $1 % 2 == 0 )) ;}

# func func -> int
filter () {
  local function_to_apply=$1
  local arg

  while read -r arg; do
    $function_to_apply $arg && echo $arg
  done;:
}

# int array
integers=( 1 2 3 4 )  
result=$(echo "${integers[*]}" | filter is_even)
declare -p result
Run Code Online (Sandbox Code Playgroud)

输出是一个字符串 ""

declare -- result=""
Run Code Online (Sandbox Code Playgroud)

预期输出是一个数组 ( 2 4 )

declare -a result ='([0]="2" [1]="4")'
Run Code Online (Sandbox Code Playgroud)

在信用到期时给予信用:

http://www.binaryphile.com/bash/2018/07/26/approach-bash-like-a-developer-part-1-intro.html

Kus*_*nda 5

您可以在一行中输入您的函数。该行将是字符串1 2 3 4,它"${integers[*]}"以默认值$IFS. 整个单行将在第一次调用readinto 时读取,arg并在调用您的函数时使用(未加引号)。由于$arg未加引号,shell 会将字符串吐在空格上,而您的函数将仅使用该初始值1,这不是偶数。这意味着echo $arg未触发。

反而:

#!/bin/bash

is_even () { (( $1 % 2 == 0 )) ;}

filter () {
  local function_to_apply="$1"
  local arg

  while read -r arg; do
    "$function_to_apply" "$arg" && echo "$arg"
  done
}

integers=( 1 2 3 4 )  
result=$(printf '%s\n' "${integers[@]}" | filter is_even)
declare -p result
Run Code Online (Sandbox Code Playgroud)

这里的主要内容是在filter函数的各个行上打印数组的元素。

这将为您提供单个字符串2\n4\n换行符在哪里)。这应该不足为奇,因为您将字符串分配给result.

如果你想取回一个数组,你可以在最近的版本中做到这一点bash

#!/bin/bash

is_even () { (( $1 % 2 == 0 )) ;}

filter () {
  local func="$1"
  local -n in_array="$2"
  local -n out_array="$3"

  local element

  out_array=()
  for element in "${in_array[@]}"; do
    if "$func" "$element"; then
        out_array+=( "$element" )
    fi
  done
}

integers=( 1 2 3 4 )
even_ints=()

filter is_even integers even_ints
declare -p even_ints
Run Code Online (Sandbox Code Playgroud)

这是在filter函数内部使用两个名称引用变量。第一个是输入数组,第二个是输出数组。

这会给你输出

declare -a even_ints=([0]="2" [1]="4")
Run Code Online (Sandbox Code Playgroud)

将值传递给filter函数的另一种方法显然是在函数的命令行上传递它们:

declare -a even_ints=([0]="2" [1]="4")
Run Code Online (Sandbox Code Playgroud)