【问题标题】:Using declare for referencing variables from an array in bash使用声明从bash中的数组引用变量
【发布时间】:2019-08-24 23:07:25
【问题描述】:

我正在尝试使用 bash 脚本遍历目录数组,以便可以使用 ls -arlt 列出目录及其时间戳、所有权等。我正在审查 bash,因此希望得到一些反馈。

对于那些间接引用,它与 declare -a 一起使用,但对于它输出的每个目录和来自 /home/user 的额外目录。

我尝试对每个目录使用declare -n 和declare -r,但不起作用。

#!/bin/bash

# Bash variables
acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )


# Function to display timestamp, ownership ...
displayInfo()
{
    for i in "${array[@]}"; do
      declare -n curArray=$i
      if [[ -d ${curArray} ]]; then
        declare -a _acpi=${curArray[0]} _apm=${curArray[1]} _xml=${curArray[2]}
        echo "Displaying folder apci: " 
        cd $_acpi
        ls -alrt
        read -p "Press enter to continue"

        echo "Displaying folder apm: " 
        cd $_apm
        ls -alrt
        read -p "Press enter to continue"

        echo "Displaying folder xml: " 
        cd $_xml
        ls -alrt
        read -p "Press enter to continue"
      else
        echo "Displayed Failed" >&2
        exit 1
      fi
    done
}
displayInfo
exit 0

它输出一个额外的目录,列出 /home/user 并且不想要那个输出。

【问题讨论】:

  • acpi、apm 和 xml 是普通变量,而不是数组;因此,当您将curArray 指向其中一个然后尝试像访问数组一样访问它时,只有${curArray[0]} 具有实际值。看起来您正在循环遍历该变量列表,然后对于每个变量再次遍历相同的事物......这没有任何意义。
  • 我已经尝试curArray[0] for ex 并且仍然在该目录上循环两次,其中一个是正确的,另一个来自不想要的 /home/user。我仍然需要为每个目录暂停。你有例子吗?
  • 你能解释一下你真正想要做什么吗?是否只是在这三个目录中的每一个上运行ls -alrt?
  • 是的,我正在尝试 ls -alrt 为每个目录验证它们的时间戳、所有权等...

标签: arrays bash loops ubuntu scripting


【解决方案1】:

这里使用了许多复杂而强大的 shell 功能,但它们的方式并不协调,也不合理。我会在一分钟内复习这些错误,首先让我简单介绍一下我是如何做到的。我将使用您可能不熟悉的一件事是使用 ${!var} 的间接变量引用——这就像使用 nameref 变量,但 IMO 更清楚发生了什么。

acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )

displayInfo()
{
    for curDirectory in "${array[@]}"; do
        if [[ -d ${!curDirectory} ]]; then
            echo "Displaying folder $curDirectory:" 
            ls -alrt "${!curDirectory}"
            read -p "Press enter to continue"
        else
            echo "Error: ${!curDirectory} does not exist or is not a directory" >&2
            exit 1
        fi
    done
}
displayInfo

(这样做的一个问题是它在每个目录之后执行“按 Enter 继续”的操作,而不仅仅是在它们之间。这可以修复,但需要更多工作。)

好的,现在看看原版出了什么问题。我对您的主要建议是尝试在心理上单步执行您的代码,看看它在做什么。将set -x 放在它之前会有所帮助,因此shell 将在运行时打印其对正在执行的操作的解释,并查看它与您的预期相比如何。让我们对displayInfo 函数做一个简短的演练:

for i in "${array[@]}"; do

这将循环array 的内容,因此在第一次通过循环时i 将被设置为“acpi”。到目前为止还不错。

declare -n curArray=$i

这会创建一个指向另一个变量acpi 的nameref 变量——这与我对${! } 所做的类似,并且到目前为止基本上是合理的。好吧,有一个例外:顾名思义,它是一个数组,但 acpi 是一个普通变量,而不是一个数组。

if [[ -d ${curArray} ]]; then

这将检查acpi 变量“/etc/acpi”的内容是否是现有目录的路径(它是)。还是不错的。

    declare -a _acpi=${curArray[0]} _apm=${curArray[1]} _xml=${curArray[2]}

这就是事情完全偏离轨道的地方。 curArray 指向变量acpi,所以${curArray[0]} 等等价于${acpi[0]} 等。但acpi 不是数组,它是一个普通变量,所以${acpi[0]} 获取它的值,@987654341 @ 和 ${acpi[2]} 得到 nothing。此外,您正在使用declare -a(声明数组),但您只是将单个值分配给_acpi、_apm 和_xml。它们被声明为数组,但您只是将它们用作普通变量(基本上与您使用 curArray -> acpi 的方式相反)。

这里还有一个更深层次的困惑。上面的for 循环正在迭代“acpi”、“apm”和“xml”,我们目前正在研究“acpi”。在此循环过程中,您应该只 处理 acpi,而不是也 尝试处理 apm 和 xml。这就是在那里有一个for 循环的意义所在。

好的,这是这里的主要问题,但让我指出一些其他我认为不好的做法:

    cd $_apm
    ls -alrt

像这样使用没有双引号的变量引用会引起解析混乱;你几乎应该总是加上双引号,比如cd "$_apm"。此外,在脚本中使用cd 是危险的,因为如果失败,脚本的其余部分将在错误的位置执行。在这种情况下,_apm 是空的,所以没有双引号它相当于只是 cd,它移动到你的主目录。这就是你得到这个结果的原因。如果您使用cd "$_apm",它会得到一个错误...但是由于您不检查它,它会继续并仍然列出一个不相关的位置。

最好完全避免cd 及其复杂性,而只使用显式路径,例如ls -alrt "$_apm"。

echo "Displayed Failed" >&2
exit 1

如果其中一个目录不存在,您真的要退出整个脚本吗?对我来说更有意义的是 return 1(它只退出函数,而不是整个脚本),或者更好的是 continue(它只是继续循环的下一次迭代——即下一个目录在名单上)。我在我的版本中保留了exit,但我建议更改它。

还有一件类似的事情:

acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )

是否有任何实际理由使用此数组 -> 变量名 -> 实际目录路径系统(以及由此产生的间接扩展或 nameref 复杂性),而不是像这样仅使用目录路径数组?

array=( /etc/acpi /etc/apm /etc/xml )

我在上面的版本中保留了间接性,但如果没有理由,我会删除复杂性。

【讨论】:

  • 我试过array=( /etc/acpi /etc/apm /etc/xml ),它输出错误的替换,所以我应该坚持间接扩展
  • @user2537246 和array=( /etc/acpi /etc/apm /etc/xml ),删除所有间接扩展。也就是说,在任何地方都使用“${curDirectory}”而不是"${!curDirectory}"。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多