【问题标题】:Replacement of "readarray -d / -t" (for splitting path) on older bash?替换旧 bash 上的“readarray -d / -t”(用于分割路径)?
【发布时间】:2021-12-08 15:05:30
【问题描述】:

我需要运行一个使用readarray -d / -t 将文件路径拆分为数组的脚本,但目标系统的readarray 不支持-d 选项(bash 版本4.2.46)

由于我不知道readarray -d / -t 的确切行为,因此我很难为其编写解决方法。 似乎无法将其替换为 IFS=/ read -a,因为包含控制字符的文件名会破坏它,如下所示:

IFS=/ read -a arr < <(echo /home/fravadona/$'\n'/underneath)
declare -p arr
# OUTPUT: declare -a arr='([0]="" [1]="home" [2]="fravadona")'

所以,我的第一个问题是,预期的结果是什么:

readarray -d / -t arr < <(echo /home/fravadona)
readarray -d / -t arr < <(echo /home/fravadona/)
readarray -d / -t arr < <(echo /home/fravadona/$'\n'/underneath)

最后,以下代码是否正确模拟readline -d / -t

filepath=/home/fravadona/$'\n'/underneath

unset arr; declare -a arr

prefix="${filepath%%/*}"
suffix="${filepath#*/}"

until [ "X$prefix" == "X$suffix" ]
do
    arr+=( "$prefix" )
    prefix="${suffix%%/*}"
    suffix="${suffix#*/}"
done

arr+=( "$prefix" )

【问题讨论】:

  • readarray 读取所有输入并生成一个数组。 read 一次读取一行,为每行生成一个数组。我注意到您有一个不可打印字符的目录。这是恶意软件吗?
  • @jordanm:这只是一个破坏可能的 `IFS=/ read -a' 解决方法的示例。
  • what's the expected result of: sooo,你为什么不运行它看看? readarray 是 bash 内部的,预期的输出是 bash 想要的,文档基本上是代码文档,而不是规范。
  • @KamilCuk:readarray -d / arr &lt;&lt;&lt; /home/KamilCuk-bash: readarray: -d: invalid option。如您所见,它在目标系统上不起作用;-)
  • 哦,所以你的意思是你手头没有 bash5 可用?在线尝试一下。 replit.com 有 bash 另外,使用 docker,docker 总是很方便。

标签: arrays bash path


【解决方案1】:

希望这能达到您的预期:

filepath=/home/fravadona/$'\n'/underneath
IFS=/ read -d "" -r -a arr < <(printf "%s" "$filepath")
declare -p arr

【讨论】:

  • 谢谢菲利普。文件路径中是否有任何字符可能会破坏此解决方法?
  • 文件路径中的任何字符都不能破坏此解决方法。
  • 此解决方法在 bash 3.2.57 (macOS) 上运行良好
  • 很高兴知道!我只在 bash 4.2 上测试过
【解决方案2】:

bash "readarray -d / -t" 的具体行为是什么?

读取所有输入。在/ 上拆分它。删除/。赋值给数组。

预期的结果是什么:

以下脚本:

readarray -d / -t arr < <(echo /home/fravadona)
declare -p arr
readarray -d / -t arr < <(echo /home/fravadona/)
declare -p arr
readarray -d / -t arr < <(echo /home/fravadona/$'\n'/underneath)
declare -p arr

输出:

declare -a arr=([0]="" [1]="home" [2]=$'fravadona\n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'\n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'\n' [4]=$'underneath\n')

以下代码是否正确模拟了 readline -d / -t?

您的代码不从标准输入读取,因此它不模拟readline。您似乎假设输入存储在某个变量中。

以下代码:

f() {
    IFS= read -d '' -r filepath
    arr=()

    prefix="${filepath%%/*}"
    suffix="${filepath#*/}"

    until [ "X$prefix" == "X$suffix" ]
    do
        arr+=( "$prefix" )
        prefix="${suffix%%/*}"
        suffix="${suffix#*/}"
    done

    arr+=( "$prefix" )
}
f < <(echo /home/fravadona)
declare -p arr
f < <(echo /home/fravadona/)
declare -p arr
f < <(echo /home/fravadona/$'\n'/underneath)
declare -p arr

输出:

declare -a arr=([0]="" [1]="home" [2]=$'fravadona\n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'\n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'\n' [4]=$'underneath\n')

所以是一样的,所以经过一些调整后的答案是肯定的。

奇怪的是,您使用 [ X 技巧 nawadays - 旧错误早已不复存在,而且无论如何 == 对于 [ 来说是非标准的。在 bash 中,只需使用 [[ "$prefix" == "$suffix" ]]

相关:Need alternative to readarray/mapfile for script on older version of Bash。我会使用零字节,例如:

f() {
   arr=();
   while IFS= read -d '' -r elem || [[ -n "$elem" ]]; do
         arr+=("$elem")
   done < <(
         tr '/' '\0'
   )
}

【讨论】:

  • 非常感谢KamilCuk;您很好地解释了一些我不知道的事情。
【解决方案3】:

[似乎]不可能用 IFS=/ read -a 替换它,因为包含控制字符的文件名会破坏它,

具体来说,包含换行符的文件名会破坏它。其他控制字符应该不是问题。

所以,我的第一个问题是,预期的结果是什么:

readarray -d / -t arr < <(echo /home/fravadona)
readarray -d / -t arr < <(echo /home/fravadona/)
readarray -d / -t arr < <(echo /home/fravadona/$'\n'/underneath)

它们分别等价于,

arr=('' home $'fravadona\n')
arr=('' home fravadona $'\n')
arr=('' home fravadona $'\n' $'underneath\n' )

请注意,每个数组都有一个空的第一个元素对应于第一“行”,并删除了尾随分隔符。

并特别注意每个结果的最后一个元素都包含一个尾随换行符。这是因为 echo 默认以 1 结束其输出,而您正在指示 readarray 使用不同的行分隔符。

那么,您可能也对这些感兴趣......

readarray -d / -t arr < <(echo -n /home/fravadona)
readarray -d / -t arr < <(echo -n /home/fravadona/)
readarray -d / -t arr < <(echo -n /home/fravadona/$'\n'/underneath)

...产生与...相同的结果

arr=('' home fravadona)
arr=('' home fravadona)
arr=('' home fravadona $'\n' underneath)

最后,以下代码是否模拟 readline -d / -t 正确吗?

unset arr; declare -a arr

prefix="${filepath%%/*}"
suffix="${filepath#*/}"

until [ "X$prefix" == "X$suffix" ]
do
    arr+=( "$prefix" )
    prefix="${suffix%%/*}"
    suffix="${suffix#*/}"
done

arr+=( "$prefix" )

不,它没有,至少因为条件[ "X$prefix" == "X$suffix" ] 是错误的。例如,如果$filepathfoo/foo,则不会执行循环迭代,arr 的最终值将只包含一个元素,而不是两个。或者如果$filepath/foo/bar/bar,那么最终结果只有两个元素,而不是三个。

这样会更好:

unset arr
declare -a arr

case $filepath in
*/) suffix=$filepath;;
*) suffix=${filepath}/;;
esac

while [[ -n "$suffix" ]]; do
  arr+=(${suffix%%/*})
  suffix=${suffix#*/}
done

【讨论】:

  • 非常感谢约翰,我的代码中的解释和错误:-)
猜你喜欢
  • 2017-05-19
  • 1970-01-01
  • 2013-12-10
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
相关资源
最近更新 更多