一个符合POSIX标准的shell应该提供如下机制来迭代字符串集合:
for x in $(seq 1 5); do
echo $x
done
但是,我如何迭代每个单词的每个字符?
一个符合POSIX标准的shell应该提供如下机制来迭代字符串集合:
for x in $(seq 1 5); do
echo $x
done
这可能有点绕,但我认为它适用于任何遵循 posix 标准的 shell。我已经在 dash
中尝试过,但我没有方便测试的 busybox。
var='ab * cd'
tmp="$var" # The loop will consume the variable, so make a temp copy first
while [ -n "$tmp" ]; do
rest="${tmp#?}" # All but the first character of the string
first="${tmp%"$rest"}" # Remove $rest, and you're left with the first character
echo "$first"
tmp="$rest"
done
输出:
a
b
*
c
d
[ -n "$tmp" ]
中的双引号是绝对必要的,如果字符串包含“*”,则first="${tmp%"$rest"}"
中的内部双引号是必需的。LC_CTYPE=C
能解决问题吗? - Charles DuffyLC_CTYPE
设置为什么;我猜busybox下的ash也是这样。如果你需要正确的逐字符处理,这会让事情变得更加复杂。 - Gordon Davissonfirst="${tmp%"$rest"}"
looks slow. alternative: first="$(echo "$tmp" | head -c1)"
- milahufirst="${tmp%"$rest"}"
在 shell 进程中完成所有操作,因此实际上速度更快。你可以自己试一下! - Gordon Davisson:
)指示 getopts 忽略非法选项并设置 OPTARG。输入中的前导连字符(-
)使 getopts 将字符串视为选项。OPTARG
,因此脚本使用参数扩展在未设置 / 为空时返回 :
。#!/bin/sh
IFS='
'
split_string () {
OPTIND=1;
while getopts ":" opt "-$1"
do echo "'${OPTARG:-:}'"
done
}
while read -r line;do
split_string "$line"
done
#!/bin/sh
IFS='
'
split_string () {
OPTIND=1;
while getopts ":" opt "$1";do
case "${OPTARG:=:}" in
([[:print:]])
[ -n "$multi" ] && echo "$multi" && multi=
echo "$OPTARG" && continue
esac
multi="$multi$OPTARG"
case "$multi" in
([[:print:]]) echo "$multi" && multi=
esac
done
[ -n "$multi" ] && echo "$multi"
}
while read -r line;do
split_string "-$line"
done
这里额外使用了case "$multi"
来检测多缓冲区是否包含可打印字符。这在像Bash和Zsh这样的shell上有效,但Dash和busybox ash不会模式匹配多字节码点,忽略locale。
这样做可以得到相对较好的结果:Dash/ash将多字节码点序列视为一个字符,但正确处理由单字节字符包围的多字节字符。
根据您的需求,不分割连续的多字节码点可能更好,因为下一个码点可能是组合字符,它修改之前的字符。
这种方法无法处理后面跟随组合字符的单字节字符情况。
split_string() { OPTIND=1; while getopts ":" opt "-$1"; do echo "'$OPTARG'"; done; }
- Koichi Nakashima这在dash
和busybox
中都有效:
echo 'ab * cd' | grep -o .
输出:
a
b
*
c
d
grep
的 -o
选项不符合 POSIX 标准。 - Luis Lavaire.grep
... - agc我正在开发一个需要使用堆栈的脚本... 因此,我们可以使用它来迭代字符串
#!/bin/sh
# posix script
pop () {
# $1 top
# $2 stack
eval $1='$(expr "'\$$2'" : "\(.\).*")'
eval $2='$(expr "'\$$2'" : ".\(.*\)" )'
}
string="ABCDEFG"
while [ "$string" != "" ]
do
pop c string
echo "--" $c
done
seq
不被 POSIX 规定;在 POSIX 中计数到 5 的一种机制可能是i=0; while [ "$i" -lt 5 ]; do echo "$i"; i=$((i + 1)); done
) - Charles Duffyseq
命令不是执行迭代的机制的一部分。但你对你的例子符合POSIX标准是正确的。 - Luis Lavaire.