因此,我有一个简单的嵌套循环,其中外部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}
是内容的长度var
。printf %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
将删除后的单个字符赋值给fname
tochar
;设置
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