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

Som*_*jee 4 bash shell-script

所以,我有一个简单的嵌套循环,其中外部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
Run Code Online (Sandbox Code Playgroud)

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

更新:

根据@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
Run Code Online (Sandbox Code Playgroud)

ilk*_*chu 9

由于您使用的是 Bash:

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

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

  • @DanieleGrassini,`for (( ))` 的内部以及 `${var:p:k}` 中的索引和偏移量是算术上下文,没有 `$` 的普通变量名在那里工作。或者你可以做类似`${word:i+1:1}` 的事情。`${#word}` 需要 `${}`,但没有办法解决这个问题。 (4认同)

gle*_*man 8

当字符串大于几百个字符时(是的,文件名不太可能),在字符串长度上使用 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
Run Code Online (Sandbox Code Playgroud)

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

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


fra*_*san 5

为了完整起见,即使问题被标记为,一个仅使用 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
Run Code Online (Sandbox Code Playgroud)

内循环的作用:

  • 设置restfname减去其第一个字符的值;

  • 分配rest从末尾删除获得的单个字符fnameto char;

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

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

顺便说一句,for file in `ls $1`应该避免。最明显的原因是,如果文件名包含任何碰巧在IFS. 更多关于这方面的信息在Bash Pitfall n。1,包括你应该做什么。