【问题标题】:Bash: how to check if directory exists and that it has files in itBash:如何检查目录是否存在以及其中是否包含文件
【发布时间】:2019-09-29 16:19:55
【问题描述】:

我想打印出所有子目录名称及其内容文件名在目录名称下方。如果子目录为空,则不打印目录名称并转到下一个子目录。我的代码的适用部分:

for dirs in "$mydir"/*
do
   if [ -d "$dirs" -type f" ] && [ "find "$dirs" -type f" ]
   then
      echo "Processing directory $dirs"
         for subfiles in $dirs/*
         do
            echo "Encoding $subfiles"
         done
   fi
done

如果我省略了第一个 if 语句的第二个条件,那么空目录会将它们的名称打印到屏幕上,并且下面会列出一个 *(我想代表目录中没有任何内容的事实)。 && 之后的部分不会导致任何错误,但它不会阻止空目录看到这部分代码的其余部分。

我怎样才能让它工作?

【问题讨论】:

  • @Cyrus 对,我知道如何自己做。我想在一个命令中检查目录中的条目是否实际上是一个目录,以及它是否有一个或多个文件。假设我的主目录包含 /Pictures,其中没有文件和一个名为 file1.txt 的文件。如果我不检查 file1.txt 是否是目录,那么脚本会回显“处理目录 file1.txt”,所以我首先要确定什么是目录,什么不是。那么如果有一个目录,我需要确保它包含文件。否则,我会收到有目录的通知,但屏幕会回显 * 为空。
  • if [[ -d "$dirs" ]] && files=( "$dirs"/* ) && [[ -e ${files[0]} || -L ${files[0]} ]]; then -- 这并不简单,但如果你想要一些可靠且性能良好的东西,那就是你得到的。
  • @CharlesDuffy 如果在"$dirs" 下按字母顺序排列的第一件事是目录,则会失败。
  • 他们用于要排除的目录的词非常明确地“空”。包含子目录的目录不为空。

标签: bash if-statement


【解决方案1】:

改变你的条件来测试空目录:

for dirs in "$mydir"/*
do
   if [ -d "$dirs" ] && [ -n "$(ls -A $dirs)" ]
   then
      echo "Processing directory $dirs"
         for subfiles in $dirs/*
         do
            echo "Encoding $subfiles"
         done
   fi
done

【讨论】:

  • 你的意思是for subfiles in "$dirs"/*?还有ls -A "$dirs"(尽管这通常不是可取的;ls 的指定不够好,无法依赖其输出或缺乏跨平台可移植性)。
  • ...不过,当您的目录名称中有空格时,它会表现得很好。
【解决方案2】:

与其查看所有内容并检查每个内容是文件还是非空目录,为什么不首先只搜索文件和非空目录?

根据您的说法,I want to print out all sub-directory names with their content file names below the directory name. 就是:

find . -mindepth 2 -maxdepth 2 ! -path . -type f -print0 |
    awk -v RS='\0' -F'/' '!seen[$(NF-1)]++{print "dir", $(NF-1)} {print "file", $NF}'

例如:

$ find . -printf '%y %p\n'
d .
f ./file
d ./tmp1
d ./tmp2
f ./tmp2/bar
f ./tmp2/foo

$ find . -mindepth 2 -maxdepth 2 ! -path . -type f -print0 |
    awk -v RS='\0' -F'/' '!seen[$(NF-1)]++{print "Processing directory", $(NF-1)} {print "Encoding", $NF}'
Processing directory tmp2
Encoding bar
Encoding foo

按摩适合。如果您需要对每个文件执行 shell 命令,则无需涉及 awk,您只需将上述内容更改为:

$ cat ../tst.sh
#!/bin/env bash
declare -A seen
while read -r -d '' line; do
    path="${line%/*}"
    dir="${path#*/}"
    file="${line##*/}"
    (( ! $(( seen[dir]++ )) )) && printf 'Processing directory %s\n' "$dir"
    printf 'Encoding %s\n' "$file"
done < <(find . -mindepth 2 -maxdepth 2 ! -path . -type f -print0)

$ ../tst.sh
Processing directory tmp2
Encoding bar
Encoding foo

【讨论】:

    【解决方案3】:

    纯 bash 中的解决方案:

    is_empty_dir() { [[ "*..." = "$(printf %s * .*)" ]]; }
    

    它应该安全地处理以. 或- 开头的文件名。

    如果当前目录为空,则退出状态为零,否则为非零。


    示例用法:

    is_empty_dir() { [[ "*..." = "$(printf %s * .*)" ]]; }
    
    if [[ -d /tmp ]]
    then
        cd /tmp
        if is_empty_dir
        then
            echo "/tmp is empty"
        else
            echo "/tmp is NOT empty"
        fi
    else
        echo "/tmp does not exist"
    fi
    

    【讨论】:

    • 请注意,function 关键字是 ksh-ism bash 支持的向后兼容性;但是,bash 的实现与 ksh 的实现并不完全兼容(这使得一些变量声明默认为本地)。最好使用符合 POSIX 的语法 is_empty_dir() { ...,不带 function 关键字;见wiki.bash-hackers.org/scripting/obsolete
    【解决方案4】:

    启用 nullglob 可能会简化一些事情:

    shopt -s nullglob
    if [ some/dir/* ]; then
        echo directory exists and is not empty
    fi
    

    当设置了 nullglob 时,不匹配任何内容的 glob 表达式返回一个空字符串。

    【讨论】:

      猜你喜欢
      • 2011-04-15
      • 1970-01-01
      • 2017-04-28
      • 2011-05-04
      • 2014-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多