【问题标题】:posix sh: is it possible to iterate through a string without using an external tool?posix sh:是否可以在不使用外部工具的情况下迭代字符串?
【发布时间】:2018-09-16 19:13:22
【问题描述】:

在 POSIX sh 中,我可以使用 while 循环遍历字符串中的每个字符并进行剪切;这是一个演示:

#!/bin/sh

lizards='geckos,anoles'

string_length="${#lizards}"

i=1
while [ "$i" -le "$string_length" ]; do
    char="$(echo "$lizards" | cut -c "$i")"
    echo "$char"
    i="$(( i + 1 ))"
done

输出是:

g
e
c
k
o
s
,
a
n
o
l
e
s

有什么方法可以不用调用外部命令吗?

【问题讨论】:

    标签: shell sh posix


    【解决方案1】:

    几年前这里有一个类似于that other guy's answerPOSIX 方法,它不是printf,而是依赖于更多变量(太多?)并且需要两个删除最小后缀模式 (${parameter%word}) 和 Remove Smallest Prefix Pattern (${parameter#word}) 参数扩展:

    splitchar(){ unset r c
                 q="$1"
                 while [ "$q" ]; do  
                     s="$q" q="${q#?}" c="${s%$q}" r="$r$c
    "
                 done
                 echo "${r%?}"; }
    

    例子:

    splitchar hello
    

    输出:

    h
    e
    l
    l
    o
    

    【讨论】:

    • 嗯。实际上它更像WQW's answer,但没有$i 计数器...
    【解决方案2】:

    您可以使用printf获取第一个字符和参数扩展以将其删除:

    #!/bin/sh
    
    lizards='geckos,anoles'
    
    while [ -n "$lizards" ]; do
        printf '%.1s\n' "$lizards"
        lizards="${lizards#?}"
    done
    

    【讨论】:

    • 非常优雅的解决方案,但是如果您想保存 'lizards' 变量以供以后使用,您可能需要添加 'iterate_characters="$lizards"' 然后将所有 'lizards' 实例更改为while 块中的“迭代字符”
    【解决方案3】:

    使用fold 而不是cut,您可以轻松地将外部命令的数量从每个字符一个减少到一个整个字符串:

    #!/bin/sh
    
    lizards='geckos,anoles'
    
    echo "$lizards" | fold -w 1
    

    我想不出纯粹使用内置函数的好方法。但我确实想到了一个丑陋的方法:

    #!/bin/sh
    
    lizards='geckos,anoles'
    
    string_length="${#lizards}"
    
    # Initialize j to length string_length-1 filled with ?'s and k to empty
    i=1
    j=
    k=
    while [ "$i" -lt "$string_length" ]; do
        j="?$j"
        i="$(( i + 1 ))"
    done
    
    i=0
    # From here on, j contains string_length-i-1 ?'s and k contains i ?'s
    while [ "$i" -lt "$string_length" ]; do
        char="${lizards#$k}"
        char="${char%$j}"
        echo "$char" # or printf '%c\n' "$char"
        j="${j#?}"
        k="?$k"
        i="$(( i + 1 ))"
    done
    

    要理解它,你必须首先理解${var#pattern}${var%pattern} 扩展,它们将$var 的开头(#)或结尾(%)与模式(glob-like,而不是正则表达式)并删除匹配的部分。 (这些取最短匹配,##%% 取最长匹配,但这并不重要。)

    因此,如果我想删除前 2 个字符的 $var,我使用 ${var#??}。如果我想删除最后 2 个字符,我使用 ${var%??}。所以我做了一个循环,每次使用可变数量的? 修剪两端。基本上它是这样做的:

    char=${lizards#}; char=${char%????????????}; echo $char
    char=${lizards#?}; char=${char%???????????}; echo $char
    char=${lizards#??}; char=${char%??????????}; echo $char
    char=${lizards#???}; char=${char%?????????}; echo $char
    char=${lizards#????}; char=${char%????????}; echo $char
    char=${lizards#?????}; char=${char%???????}; echo $char
    char=${lizards#??????}; char=${char%??????}; echo $char
    char=${lizards#???????}; char=${char%?????}; echo $char
    char=${lizards#????????}; char=${char%????}; echo $char
    char=${lizards#?????????}; char=${char%???}; echo $char
    char=${lizards#??????????}; char=${char%??}; echo $char
    char=${lizards#???????????}; char=${char%?}; echo $char
    char=${lizards#????????????}; char=${char%}; echo $char
    

    我特别喜欢编写j="${j#?}" 行,它使用${var#pattern} 构造从变量中删除一个字符,该字符将用作下一个${var%pattern} 构造中的模式。

    实际上,我想我会使用fold

    【讨论】:

      猜你喜欢
      • 2023-03-21
      • 1970-01-01
      • 2011-04-21
      • 2021-03-23
      • 2022-09-27
      • 2010-12-22
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      相关资源
      最近更新 更多