Общая функция для циклического перебора входных данных и выполнения команды в bash?

Вы также можете попробовать:

ssh -t user@remoteip <<'EOF'
command1
command2
command3
EOF

Хотя, вероятно, было бы лучше сохранить скрипт с нужными вам командами на удаленной машине и просто вызвать его после входа в систему.

2
22.06.2019, 20:43
3 ответа

Вы неправильно вызвали массив. $arrбудет расширяться только до первого элемента в массиве, а $arr[@]будет расширяться до первого элемента с добавленной к нему литеральной строкой [@].

Для вызова всех элементов массива используйте:"${arr[@]}"

Другая проблема, с которой вы столкнулись, заключается в том, что $2содержит только второй позиционный параметр, где вы пытаетесь перебрать 3-й, 4-й, 5-й и т. д. Все они будут сохранены в $@.

Чтобы достичь своей цели, вы можете сделать что-то вроде:

function loop {
    local command=$1
    shift
    for i in "$@"; do
        "$command" "$i"
    done
}

Это установит commandна первый позиционный параметр, а затем сдвинет его так, чтобы $@можно было использовать для циклического перебора оставшихся. Тогда вам просто нужно правильно вызвать массив:

$ declare -a arr=("element1" "element2" "element3")
$ loop echo "${arr[@]}"
element1
element2
element3
$ loop printf 'hello ' 'world\n'
hello world
$ loop touch file1 file2 file3
$ ls
file1  file2  file3

Если вы хотите, чтобы эта функция могла принимать различные разделители, вы можете сделать что-то вроде:

function loop {
    local command=$1
    local delim=$2
    shift 2
    set -- $(tr "$delim" ' ' <<<"$@")
    for i in "$@"; do
        "$command" "$i"
    done
}

Это означает, что вы должны указать, какой разделитель используется через второй параметр, например:

$ loop echo '|' 'one|two|three'
one
two
three
$ loop echo '\n' "$(printf '%s\n' 'one' 'two' 'three')"
one
two
three

Однако в этом есть некоторые ошибки. (Если вы укажете пользовательский разделитель, он все равно будет разделять пробелами)

4
27.01.2020, 21:53

В вашем коде есть синтаксическая ошибка. В вызове вашей функции loopиспользуйте "${arr[@]}", чтобы расширить массив arrдо списка каждого из его элементов, заключенных в кавычки. Это то, что вы делаете в показанном цикле for, и это также то, что вы должны делать при вызове функции:

loop echo "${arr[@]}"

Также обратите внимание, что ваша функция должна выбрать имя команды, которую вы передаете, и что затем она должна пройтись по оставшимся аргументам:

loop () {
    local cmd=$1; shift
    for arg do
        "$cmd" "$arg"
    done
}

Здесь мы присваиваем первый аргумент переменнойcmd(это команда ), затем мы shiftэтот аргумент убираем из списка аргументов. Список аргументов теперь содержит только те строки, которые вы хотите перебрать.

Затем цикл по очереди вызывает команду для каждого оставшегося аргумента.

Это повторяет назначение утилиты xargsв ограниченном виде, и функция может быть повторно -реализована с помощью этой утилиты (, если только вы не ожидаете, что один из аргументов будет содержать встроенную новую строку, в которой случае вам придется настроить параметры наxargs):

loop () {
    local cmd=$1; shift
    printf '%s\n' "$@" | xargs -L 1 "$cmd"
}

Поскольку утилита xargsожидает, что данные будут доставлены через стандартный ввод, мы согласовываем это с помощью printfи печатаем каждый аргумент, переданный loop, в отдельной строке (, сообщая xargsвызовите данную утилиту один раз для каждого аргумента с разделителями -новой строки, используя-L 1).

Это позволило бы нам использовать другие функции некоторых реализаций xargs, например запуск параллельных процессов с использованием -P n, где n— некоторое количество процессов, которые должны выполняться параллельно.

Тестирование:

$ cat script.sh
loop () {
    local cmd=$1; shift
    printf '%s\n' "$@" | xargs -L 1 "$cmd"
}

arr1=(1 2 3)
arr2=("hello world" "home sweet home")

loop echo "${arr1[@]}"
loop echo "${arr2[@]}"
$ bash script.sh
1
2
3
hello world
home sweet home
1
27.01.2020, 21:53

Ваш цикл очень похож на GNU Parallel:

declare -a arr=("1 a" "2 b" "3 c")
var1="1 a,2 b,3 c"
var2="1-a 2-b 3-c"
parallel -j1 echo ::: "${arr[@]}"
parallel -j1 -d, echo ::: "$var1"
parallel -j1 echo ::: $var2

-j1принудительно запускает одно задание за раз.

1
27.01.2020, 21:53

Теги

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