Как установить переменную для переменной внутри переменной в сценарии оболочки - PullRequest
0 голосов
/ 22 марта 2020

Мне нужно написать сценарий оболочки POSIX, который изменит конфигурацию системы. Прежде чем сделать это, я хочу убедиться, что есть резервные копии любого файла, который я редактирую. Для этого сценария необходимо, чтобы он использовал dmenu для запроса пользователя, если он установлен, и read, если нет. Я хочу одну функцию (названную communicate ниже), которая будет автоматически обрабатывать это для меня на основе переменной, которая устанавливается при запуске, $ dmenu .

У меня возникают проблемы с записью в переменная внутри переменной, как показано ниже:

#!/usr/bin/env sh

[ $(command -v dmenu 2>/dev/null) ] && dmenu='true'

communicate(){
    description="$1"; options="$2"; outcome="$3"
    if [ $dmenu ]; then
        echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
    else
        printf "$description $options "; read $outcome
    fi
}

backup(){
    [ $1 ] && file="$1" || communicate 'Enter file: ' '' 'file'
    [ ! -f $file ] && backup "$1"
    cp "$file" "$file.bak"
}

select_interface(){
    [ $1 ] && interface="$1" || communicate 'Select interface:' "$interfaces" 'interface'
}

backup хочет сохранить пользовательский ввод в переменную с именем $ file , тогда как позже select_interface хочет сохранить в переменная с именем $ interface . если dmenu не установлен, запись в $ result работает нормально с оператором else, тогда как если он установлен, я не могу заставить команду read срабатывать при передаче результата dmenu через перенаправление STDIN в чтение, которое работает вне сценария.

Может кто-то увидеть, что я делаю неправильно или как я мог бы сделать это лучше? Мне нужно, чтобы все было в одной функции общаться , выступая в качестве агента связи с пользователем.

1 Ответ

1 голос
/ 25 марта 2020

Оператор

echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome

, являющийся каналом, заставляет оболочку реализовать echo и read как 2 отдельных процесса. read по-прежнему является разветвленной оболочкой, и она по-прежнему устанавливает переменную $outcome, но она устанавливает ее только в разветвленной оболочке, а не в разветвленной (родительской) оболочке.

Технически правильный способ сделать это:

eval $outcome=\$\(printf "$options" \| dmenu -i -p "$description"\)'

НО Я бы не советовал eval для чего-либо, кроме одноразового кода.

Я также советую против функций, которые принимают имена переменных для установки, это довольно трудно понять правильно.

Более чистый способ сделать это:

#!/usr/bin/env sh

if [ $(command -v dmenu 2>/dev/null) ]; then
    communicate() {
        description="$1"
        options="$2"
        # also fixed this bug with the menu selection, each option needs to be in a new line
        printf "%s\n" $options | dmenu -i -p "${description}:"
    }
else
    communicate() {
        description="$1"
        options="$2"
        if [ -n "$options" ]; then
            optstring="options: ${options}; "
        else
            optstring=""
        fi
        read -p "${optstring}${description}: " outcome
        echo $outcome
    }
fi

backup() {
    if [ -n "$1" ]; then
        file="$1"
    else
        file=$(communicate 'Enter file')
    fi
    if [ -f "$file" ]; then
        cp "$file" "${file}.bak"
    else
        backup
    fi
}

select_interface() {
    if [ -n "$1" ]; then
        interface="$1"
    else
        interface=$(communicate "Enter interface" "$interfaces")
    fi
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...