循环时避免扩展:for $ in foo

时间:2015-05-22 18:49:25

标签: bash shell for-loop wildcard expansion

我必须计算目录中可执行文件的数量。

我已经想出了一种不同的方法(通过写入文件然后搜索文件,但这有点难看)。

首先找到我的解决方案是这个(第一个参数是目录路径):

#!/bin/bash

noe=0

files=`ls -F $1` # because the -F option appends an indicator
                 # to the file, for an executable it's an '*'
                 # so if there is an executable 'script.sh' in the dir
                 # the output will be like this: 'script.sh*'

for i in $files
do
    if [ `echo "$i" | grep '*$'` ] #should search for an '*' at the end...
    then
        let noe += 1
    fi
done

echo $noe

这不起作用,因为' *'在for循环中被省略。

(for循环中的echo命令输出最终没有' *'的文件名,但是当参数在&#34时,在for循环之外正常工作;&#34 ;)

关于这个here也有类似的问题,我已经设法让答案适应我的情况,但是没有解释为什么它不能用于。 +我不完全理解为什么while循环中还有<

...
done < <(ls -F $1) 
     ^
     |_ I know that this means redirect to file to loop
        Does the second < mean that we are redirecting the
        standard input file? (this might be a stupid question)

另一个问题: 有没有办法绕过这个for循环,为什么?

3 个答案:

答案 0 :(得分:6)

在任何情况下involve ls都不应该解决这个问题。

您可以使用for循环迭代文件并使用-x测试来确定文件是否可执行。但是,目录通常也是可执行的(如果不是,则不能输入它们,例如使用cd),因此根据您是否要在结果中包含目录,您可能需要-d也测试。例如:

for file in ./*; do
    if [[ -x $file && ! -d $file ]]; then
        printf '<%s> is an executable file that is not a directory\n' "$file"
        (( count++ ))
    fi
done
printf '%d executable files found\n' "$count"

至于第二个问题:

...
done < <(ls -F $1) 
     ^
     |_ I know that this means redirect to file to loop
        Does the second < mean that we are redirecting the
        standard input file? (this might be a stupid question)

<(...)是进程替换,并被文件名替换为fd或命名管道(取决于操作系统支持的内容)。从此fd或命名管道读取的任何进程都将在<(...)

中获取命令的输出(stdout)

你可以通过echo使用它来看到这个:

$ echo <(true)  # turns into  echo /dev/fd/63
/dev/fd/63

因此,在您的情况下,done < <(ls...)变成类似done < /dev/fd/63的内容,这是您已经熟悉的标准文件重定向。

答案 1 :(得分:0)

使用find查找文件:

#!/bin/bash
find "$1" -type f -executable -maxdepth 1 | wc -l

答案 2 :(得分:0)

for x in $foo; do ...

...没有循环遍历数组;它循环遍历字符串。因此,要让循环执行多次,你必须将字符串放入某种扩展,即使该扩展只是字符串拆分。

如果你关闭了所有扩展,你就会得到这个:

for x in "$foo"; do ...

...将执行一次,使循环无效。

现在,这里有一个中间位置:您可以暂时关闭glob扩展,但保持字符串拆分:

set -f              # disable all globbing
for x in $foo; do   # expand without globbing
  ...
done
set +f              # turn globbing back on

请注意,除非您控制变量IFS的值,否则其行为不一定是可预测的,该变量控制发生字符串拆分的字符。

如果你想构建一个数组,你可以这样做:

array=( "first element" "second element" "third element" )
for x in "${array[@]}"; do ...

...在的情况下,您可以安全地使用for