如何在Shell脚本中将变量设置为变量中的变量

问题描述 投票:0回答:1

我需要编写一个POSIX Shell脚本,它将更改系统配置。在这样做之前,我想确保我编辑的任何文件都有备份。此脚本的要求是使用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,则在else语句中写入$ outcome可以正常工作,而如果已安装,当通过STDIN重定向传递read的结果时,似乎无法获得dmenu命令来触发读入,可以在脚本之外使用。

有人可以看到我在做错什么,还是可以做得更好?我需要所有这些都在一个功能communicate中,充当与用户的通信代理。

linux shell posix
1个回答
0
投票

声明

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

是管道,使外壳将echoread实现为2个单独的进程。 read仍然是一个派生的shell,它仍然设置变量$outcome,但是它仅在派生的shell中设置它,而不是在派生的(父)shell中设置它。

技术上正确的方法是:

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

[BUT我建议不要使用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
}
© www.soinside.com 2019 - 2024. All rights reserved.