【问题标题】:Loop through files in directory specified using argument循环遍历使用参数指定的目录中的文件
【发布时间】:2020-10-02 03:46:16
【问题描述】:

我正在尝试遍历目录中的文件,该目录作为参数传递。我目前在test.sh中保存了以下脚本:

#!/bin/bash
for filename in "$1"/*; do
    echo "File:"
    echo $filename
done

我正在使用:

sh test.sh path/to/loop/over

但是,上面没有输出path/to/loop/over目录下的文件,而是输出:

File:
path/to/loop/over/*

我猜它会将path/to/loop/over/* 解释为字符串而不是目录。我的预期输出如下:

File:
foo.txt
File:
bar.txt

其中foo.txtbar.txtpath/to/loop/over/ 目录中的文件。我发现this answer 建议在$1 之后添加/*,但是,这似乎没有帮助(these suggestions 也没有)

【问题讨论】:

  • 感谢正确引用"$1"

标签: bash shell


【解决方案1】:

如果目录为空或拼写错误,就会发生这种情况。如果没有匹配,shell(在其默认配置中)根本不会扩展通配符。 (您可以在 Bash 中使用 shopt -s nullglob 控制这一点;使用此选项,不匹配任何内容的通配符将被简单地删除。)

您可以自己轻松地验证这一点。在一个有四个文件的目录中,

sh$ echo *
a       file    or      two

sh$ echo [ot]*
or      two

sh$ echo n*
n*

在 Bash 中,

bash$ echo n*
n*

bash$ shopt -s nullglob

bash$ echo n*

我猜你对当前工作目录如何影响目录名称的解析感到困惑;也许阅读Difference between ./ and ~/

【讨论】:

    【解决方案2】:

    遍历目录的内容

    兼容的答案(不仅是 bash)

    由于这个问题被标记为,因此有一种POSIX 兼容 方式:

    #!/bin/sh
    
    for file in "$1"/* ;do
        [ -f "$file" ] && echo "Process '$file'."
    done
    

    就足够了(使用包含空格的文件名):

    $ myscript.sh /path/to/dir
    Process '/path/to/dir/foo'.
    Process '/path/to/dir/bar'.
    Process '/path/to/dir/foo bar'.
    

    使用 any 可以很好地工作。使用bashkshdashzshbusybox sh 测试。

    #!/bin/sh
    
    cd "$1" || exit 1
    for file in * ;do
        [ -f "$file" ] && echo "Process '$file'."
    done
    

    此版本不会打印路径:

    $ myscript.sh /path/to/dir
    Process 'foo'.
    Process 'bar'.
    Process 'foo bar'.
    

    一些 方式

    简介

    我不喜欢在不需要时使用shopt...(此更改标准 bash 行为并降低脚本的可读性)。

    使用标准 bash 有一种优雅的方法可以做到这一点,不需要 shopt

    当然,以前的答案在 下工作正常,但是。有一些 让你的脚本更强大、更灵活、更漂亮、更详细……

    样本

    #!/bin/bash
    
    die() { echo >&2 "$0 ERROR: $@";exit 1;}            # Emergency exit function
    
    [ "$1" ] || die "Argument missing."                 # Exit unless argument submitted
    
    [ -d "$1" ] || die "Arg '$1' is not a directory."   # Exit if argument is not dir
    
    cd "$1" || die "Can't access '$1'."                 # Exit unless access dir.
    
    files=(*)                                           # All files names in array $files
    
    [ -f "$files" ] || die "No files found."            # Exit if no files found
    
    for file in "${files[@]}";do                        # foreach file:
        echo Process "$file"                            #   Process file
    done
    

    说明:考虑 globbing真实文件

    做的时候:

    files=(/path/to/dir/*)
    

    变量$files变成了一个数组,其中包含/path/to/dir/下的所有文件:

    declare -p files
    declare -a files=([0]="/path/to/dir/bar" [1]="/path/to/dir/baz" [2]="/path/to/dir/foo")
    

    但是如果没有匹配 glob 模式,star 将不会被替换,数组变为:

    declare -p files
    declare -a files=([0]="/path/to/dir/*")
    

    从那里。寻找$files 就像寻找${files[0]} 即:数组中的第一个字段。所以

    [ -f "$files" ] || die "No files found."
    

    将执行die 函数,除非数组files第一个字段文件[ -e "$files" ] 检查现有条目[ -d "$files" ] 检查现有的目录,等等...参见man bashhelp test)。

    但是你可以用一些基于字符串的测试替换这个文件系统测试,比如:

    [ "$files" = "/path/to/dir/*" ] && die "No files found."
    

    或者,使用数组长度:

    ((${#files[@]}==1)) && [ "${files##*/}" = "*" ] && die "No files found."
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      相关资源
      最近更新 更多