替换旧 bash 上的"readarray -d / -t"(用于拆分路径)?



我需要运行一个脚本,使用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" )

希望这能达到你的期望:

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

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]=$'fravadonan')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'n' [4]=$'underneathn')

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

你的代码不能从stdin中读取,所以它不能模拟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]=$'fravadonan')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'n')
declare -a arr=([0]="" [1]="home" [2]="fravadona" [3]=$'n' [4]=$'underneathn')

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

很奇怪,你总是使用[ X的技巧-旧的bug早就消失了,无论如何==[的非标准…在bash中,只需使用[[ "$prefix" == "$suffix" ]]

相关:需要替代readarray/mapfile在旧版本的Bash脚本。我会选择零字节,比如:

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

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 $'fravadonan')
arr=('' home fravadona $'n')
arr=('' home fravadona $'n' $'underneathn' )

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

特别注意,每个结果的最后一个元素都包含一个尾随换行符。这是因为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

最新更新