Почему моя функция фильтрации в Bash не работает?

Источник~

Команда nohup budgie-panel --replace&. (Это для текущей версии № 10, GTK :будущая версия 11 , как говорят, будет основана на qt --, и это будет другая история.)

Чтобы вызвать это одним или двумя щелчками мыши, используйте ярлык -или добавьте средство запуска -с помощью gedit:

.

gedit ~/.local/share/applications/restart-budgie.desktop

, затем вставьте строки ниже -и сохраните:

[Desktop Entry]
Type=Application
Name=Restart budgie desktop
Icon=preferences-desktop
Categories=System;Settings;
Exec=sh -c 'nohup budgie-panel --replace&'

0
25.03.2020, 13:02
2 ответа

Вы вводите свою функцию в одной строке. Строка будет строкой 1 2 3 4, которая расширяется до "${integers[*]}"со значением по умолчанию $IFS. Вся эта единственная строка будет прочитана при первом вызове readв argи использована (без кавычек )при вызове вашей функции. Поскольку $argне заключен в кавычки, оболочка будет плевать строку на пробелы, и ваша функция будет использовать только этот начальный 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

Главное здесь — вывести элементы массива на отдельные строки в функцию 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

Здесь используются две переменные ссылки на имя внутри функции filter. Первый — это входной массив, а второй — выходной массив.

Это даст вам результат

declare -a even_ints=([0]="2" [1]="4")

Другой способ передать значения в функцию filter, очевидно, состоит в том, чтобы передать их в командной строке функции:

#!/bin/bash

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

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

  local element

  out_array=()
  for element do
    if "$func" "$element"; then
        out_array+=( "$element" )
    fi
  done
}

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

filter is_even even_ints "${integers[@]}"
declare -p even_ints
5
28.04.2021, 23:19

1. @Kusalananda's filterне сможет фильтровать массив на месте; если один и тот же массив указан как источник и место назначения, он просто усекает его.

Это можно легко исправить, переписав функцию следующим образом:

filter() {
        local cb=$1 i j a; local -n src=$2 dst=$3
        for a in "${src[@]}"; do
                "$cb" "$a" && dst[i++]=$a
        done
        for((j=${#dst[@]}; j>=i; j--)); do
                unset dst[j]
        done
}

2. Однако такие демонстрации/вызовы (, например, "Как сделать функциональное программирование в bash" )совершенно бессмысленны . Даже по сравнению с другими языками очень высокого -уровня, такими как perl или javascript, bash невероятно медленный. Использование bash для разбора/фильтрации данных вместо вызова других утилит вроде grepможет оказаться на порядки медленнее.

Используя проблему из вопроса, решение grep будет в 3 раза быстрее даже с ничтожным массивом из 1000 -элементов и при фильтрации собственных массивов bash , а не некоторых внешних файлов. И это несмотря на то, что нужно разветвить около 3 процессов и выполнить внешний двоичный файл. См. приведенный ниже пример (с использованием массива из 100 000 элементов ):

.
bash filter.sh 100000
=== bash_filter ===

real    0m1.037s
user    0m1.036s
sys     0m0.001s
=== grep_filter ===

real    0m0.302s
user    0m0.226s
sys     0m0.189s

фильтр.ш:

is_even () { (( $1 % 2 == 0 )) ;}
bash_filter() {
    local cb=$1 i j a; local -n src=$2 dst=$3
    for a in "${src[@]}"; do
        "$cb" "$a" && dst[i++]=$a
    done
    for((j=${#dst[@]}; j>=i; j--)); do
        unset dst[j]
    done
}
grep_filter() {
    local flt=$1; local -n src=$2 dst=$3
    dst=($(printf '%d\n' "${src[@]}" | grep "$flt"))

}
timeit(){
    echo "=== $1 ==="
    time "$@" inlist outlist
    [ "${#outlist[@]}" -lt 20 ] && echo "${outlist[@]}"
}

inlist=($(seq "${1-100000}"))
timeit bash_filter is_even
timeit grep_filter '[02468]$'
1
28.04.2021, 23:19

Теги

Похожие вопросы