是否可以引用中的索引$@
?我在GrayCat 的 wiki 中的任何地方都找不到任何可使用的参考,并且高级脚本指南和其他人在修改它之前将其分配给不同的变量。
$ echo ${@[0]}
-bash: ${@[0]}: bad substitution
Run Code Online (Sandbox Code Playgroud)
目标是DRY:第一个参数用于一件事,其余用于其他用途,我想避免复制代码以进行规范化、$@
数组或为此创建单独的函数(尽管在此点它可能是最简单的方法)。
说明:目的是修改可变长度 的值,$@
使代码更易于调试。当前版本对我来说有点太hacky了,尽管它甚至适用于奇怪的路径,例如
$'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'
Run Code Online (Sandbox Code Playgroud)
更新:看起来这是不可能的。代码现在同时使用代码和数据重复,但至少它可以工作:
path_common()
{
# Get the deepest common path.
local common_path="$(echo -n "${1:-}x" | tr -s '/')"
common_path="${common_path%x}"
shift # $1 is obviously part of $1
local path
while [ -n "${1+defined}" ]
do
path="$(echo -n "${1}x" | tr -s '/')"
path="${path%x}"
if [[ "${path%/}/" = "${common_path%/}/"* ]]
then
shift
else
new_common_path="${common_path%/*}"
[ "$new_common_path" = "$common_path" ] && return 1 # Dead end
common_path="$new_common_path"
fi
done
printf %s "$common_path"
}
Run Code Online (Sandbox Code Playgroud)
赏金去的人谁可以摆脱的重复代码,以折叠重复斜线或重复的数据保存$1
及其它参数,或两者同时保持代码的一个合理的规模和成功的所有的单元测试:
test "$(path_common /a/b/c/d /a/b/e/f; echo x)" = /a/bx
test "$(path_common /long/names/foo /long/names/bar; echo x)" = /long/namesx
test "$(path_common / /a/b/c; echo x)" = /x
test "$(path_common a/b/c/d a/b/e/f ; echo x)" = a/bx
test "$(path_common ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
test "$(path_common $'\n/\n/\n' $'\n/\n'; echo x)" = $'\n/\n'x
test "$(path_common --/-- --; echo x)" = '--x'
test "$(path_common '' ''; echo x)" = x
test "$(path_common /foo/bar ''; echo x)" = x
test "$(path_common /foo /fo; echo x)" = x
test "$(path_common $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n' $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'; echo x)" = $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'x
test "$(path_common /foo/bar //foo//bar//baz; echo x)" = /foo/barx
test "$(path_common foo foo; echo x)" = foox
test "$(path_common /fo /foo; echo x)" = x
Run Code Online (Sandbox Code Playgroud)
Gil*_*il' 17
为了规范所有参数中的斜线,我将使用旋转参数技巧:$1
移开,变换它并将结果放在参数列表的末尾。如果你这样做的次数和参数一样多,你就已经转换了所有的参数,而且你已经把它们按顺序恢复了。
对于代码的第二部分,我将您的逻辑更改为不那么混乱:外循环迭代参数,内循环迭代路径组件。for x; do … done
迭代位置参数,这是一个方便的习惯用法。我使用符合 POSIX 的方式将字符串与模式匹配:case
构造。
使用 dash 0.5.5.1、pdksh 5.2.14、bash 3.2.39、bash 4.1.5、ksh 93s+、zsh 4.3.10 进行测试。
旁注:bash 4.1.5 中似乎有一个错误(3.2 中没有):如果 case 模式是"${common_path%/}"/*
,则其中一个测试失败。
posix_path_common () {
for tmp; do
tmp=$(printf %s. "$1" | tr -s "/")
set -- "$@" "${tmp%.}"
shift
done
common_path=$1; shift
for tmp; do
while case ${tmp%/}/ in "${common_path%/}/"*) false;; esac; do
new_common_path=${common_path%/*}
if [ "$new_common_path" = "$common_path" ]; then return 1; fi
common_path=$new_common_path
done
done
printf %s "$common_path"
}
Run Code Online (Sandbox Code Playgroud)
如果您使用 bash(或 ksh),则可以使用数组——我不明白为什么您似乎将自己限制在位置参数上。这是一个使用数组的版本。我不得不承认它并不比 POSIX 版本特别清晰,但它确实避免了最初的 n^2 改组。
对于斜线规范化部分,我使用 ksh93 构造${foo//PATTERN/REPLACEMENT}
构造来替换所有出现的PATTERN
in $foo
by REPLACEMENT
。模式是+(\/)
匹配一个或多个斜线;在 bash 下,shopt -s extglob
必须有效(相当于,以 开始 bash bash -O extglob
)。该构造set ${!a[@]}
将位置参数设置为数组的下标列表a
。这提供了一种迭代数组元素的便捷方法。
对于第二部分,我使用与 POSIX 版本相同的循环逻辑。这一次,我可以使用,[[ … ]]
因为这里针对的所有 shell 都支持它。
使用 bash 3.2.39、bash 4.1.5、ksh 93s+ 进行测试。
array_path_common () {
typeset a i tmp common_path new_common_path
a=("$@")
set ${!a[@]}
for i; do
a[$i]=${a[$i]//+(\/)//}
done
common_path=${a[$1]}; shift
for tmp; do
tmp=${a[$tmp]}
while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
new_common_path="${common_path%/*}"
if [[ $new_common_path = $common_path ]]; then return 1; fi
common_path="$new_common_path"
done
done
printf %s "$common_path"
}
Run Code Online (Sandbox Code Playgroud)
遗憾的是,zsh 缺乏按${!array[@]}
原样执行 ksh93 版本的功能。幸运的是,zsh 有两个特性使第一部分变得轻而易举。您可以像索引@
数组一样索引位置参数,因此无需使用中间数组。并且 zsh 有一个数组迭代构造:"${(@)array//PATTERN/REPLACEMENT}"
依次对每个数组元素执行模式替换并计算结果数组(令人困惑的是,即使结果是多个单词,您也需要双引号;这是 的概括"$@"
)。第二部分基本不变。
zsh_path_common () {
setopt local_options extended_glob
local tmp common_path new_common_path
set -- "${(@)@//\/##//}"
common_path=$1; shift
for tmp; do
while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
new_common_path="${common_path%/*}"
if [[ $new_common_path = $common_path ]]; then return 1; fi
common_path="$new_common_path"
done
done
printf %s "$common_path"
}
Run Code Online (Sandbox Code Playgroud)
我的解决方案经过最低限度的测试和评论。我已经更改了您的测试用例的语法,以便在没有的 shell 下解析$'…'
并以更方便的方式报告失败。
do_test () {
if test "$@"; then echo 0; else echo $? "$@"; failed=$(($failed+1)); fi
}
run_tests () {
function_to_test=$1; shift
failed=0
do_test "$($function_to_test /a/b/c/d /a/b/e/f; echo x)" = /a/bx
do_test "$($function_to_test /long/names/foo /long/names/bar; echo x)" = /long/namesx
do_test "$($function_to_test / /a/b/c; echo x)" = /x
do_test "$($function_to_test a/b/c/d a/b/e/f ; echo x)" = a/bx
do_test "$($function_to_test ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
do_test "$($function_to_test '
/
/
' '
/
'; echo x)" = '
/
'x
do_test "$($function_to_test --/-- --; echo x)" = '--x'
do_test "$($function_to_test '' ''; echo x)" = x
do_test "$($function_to_test /foo/bar ''; echo x)" = x
do_test "$($function_to_test /foo /fo; echo x)" = x
do_test "$($function_to_test '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\''
' '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\''
'; echo x)" = '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\''
'x
do_test "$($function_to_test /foo/bar //foo//bar//baz; echo x)" = /foo/barx
do_test "$($function_to_test foo foo; echo x)" = foox
do_test "$($function_to_test /fo /foo; echo x)" = x
if [ $failed -ne 0 ]; then echo $failed failures; return 1; fi
}
Run Code Online (Sandbox Code Playgroud)
为什么不直接使用 $1、$2 .. $9、${10}、${11}.. 等等?它比您尝试做的更干燥:)
更多关于 $ number和 $@之间的关系:
$@ 可以被认为是“包含所有参数的数组的所有元素”的简写
所以,$@ 是 ${args[@]} 的一种简写(这里的 args 是一个包含所有参数的“虚拟”数组——不是真正的变量,请注意)
$1 是 ${args[1]},$2 是 ${args[2]},依此类推。
当您点击 [9] 时,请使用大括号:${10} 是 ${args[10]},${11} 是 ${args[11]},依此类推。
argnum=3 # You want to get the 3rd arg
do-something ${!argnum} # Do something with the 3rd arg
Run Code Online (Sandbox Code Playgroud)
例子:
argc=$#
for (( argn=1; argn<=argc; argn++)); do
if [[ ${!argn} == "foo" ]]; then
echo "Argument $argn of $argc is 'foo'"
fi
done
Run Code Online (Sandbox Code Playgroud)
第一个参数用于一件事,其余的用于另一件事,
我想你想要的是 shift
$ set one two three four five
$ echo $@
one two three four five
$ echo $1
one
$ foo=$1
$ echo $foo
one
$ shift
$ echo $@
two three four five
$ shift 2
$ echo $@
four five
$ echo $1
four
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6505 次 |
最近记录: |