如何循环遍历文件名的每个字符?

如何循环遍历文件名的每个字符?

因此,我有一个简单的嵌套循环,其中外部for循环遍历目录中的所有文件,内部for循环遍历这些文件名的所有字符。

#!/bin/bash

if [ $# -lt 1 ]
then
        echo "Please provide an argument"
        exit
fi

for file in `ls $1`
do
        for ch in $file
        do
                echo $ch
        done
done

上面的脚本不起作用。内部循环不会循环遍历文件名中的所有字符,而是循环遍历整个内容。

更新:

根据 @ilkkachu 的回答,我能够想出以下脚本并且它按预期工作。但我很好奇我们能不使用循环for...in来迭代字符串吗?

#!/bin/bash

if [ $# -lt 1 ]
then
        echo "Please provide an argument"
        exit
fi

for file in `ls $1`; do
        for ((i=0; i<${#file}; i++)); do
                printf "%q\n" "${file:i:1}"
        done
done

答案1

由于您使用的是 Bash:

#!/bin/bash
word=foobar
for ((i=0; i < ${#word}; i++)); do
   printf "char: %q\n" "${word:i:1}" 
done

${var:p:k}给出kvar从位置开始的字符p${#var}是内容的长度varprintf %q以明确的格式打印输出,例如换行符显示为$'\n'

答案2

当字符串变得超过几百个字符时(是的,对于文件名来说不太可能),在字符串长度上使用 for 循环并提取索引处的字符i会变得非常慢。

这个答案使用了先进的 bash 技术:

while IFS= read -r -d "" -n 1 char; do
    # do something with char, like adding it to an array
    chars+=( "$char" )
done < <(printf '%s' "$string")

# inspect the array
declare -p chars

这使用了一个流程替代将字符串重定向到 while-read 循环中。我用来printf避免在字符串末尾添加换行符。使用进程替换的主要优点printf ... | while read ...是循环在当前的外壳,不是壳。

我曾经对缓慢的程度感到好奇对其进行基准测试

答案3

为了完整起见,即使问题被标记为,仅使用 POSIX shell 功能的替代方案:

#!/bin/sh
for fname
do
  while [ "${#fname}" -ge 1 ]
  do
    rest=${fname#?} char=${fname%"$rest"} fname=$rest
    printf '%s\n' "$char"       # Do something with the current character
  done
done

内循环的作用:

  • 设置为减去其第一个字符rest的值;fname

  • rest将删除后的单个字符赋值给fnameto char

  • 设置fname为 的值rest并重复,直到处理完所有字符。

请注意 , 中的引号${fname%"$rest"},以防止$rest的扩展被用作模式。

顺便说一句,for file in `ls $1`应该避免。最明显的原因是,如果文件名包含恰好位于IFS.有关此内容的更多信息,请访问Bash 陷阱1,包括您应该做什么。

答案4

使用bash字符串切片运算符:

s="string"
for c in $(seq 0 $((${#s}-1)));  do echo "${s:c:1}"; done

s
t
r
i
n
g

应用于您的脚本可以是:

#!/bin/bash
for f in *; do
    echo "Char in $f:"
    for i in $(seq 0 $((${#f}-1))); do
        echo "${f:i:1}"
    done
done

相关内容