循环遍历子目录以获取文件并对它们执行某些操作

循环遍历子目录以获取文件并对它们执行某些操作

我试图循环遍历文件夹以获取文件并对它们执行某些操作,将输出重定向到与文件同名的文本文件。我尝试使用“查找”-

cd /filepath/orig/v1

for dir in $(find . -type d); do
  cd $dir
  for subdir in $(find . -type d); do
          cd $subdir
          for file in `ls`; do
                  echo $file
                  touch $file.txt
                  cdo info $file > $file.txt
          done
  done
done

但这是行不通的。目录结构类似于 - /filepath/orig/v1/level1/level2/file.nc,但子目录可以有两级以上。

答案1

为此不需要循环。查找将完成这一切。

find . -type f ! -name '*.txt' -print -exec sh -c 'cdo info {} > {}.txt' \;

请注意,这会破坏现有的 .txt 文件,您可能需要使用比“not *.txt”更具体的文件名过滤器

答案2

如果您有两级固定目录结构:

shopt -s dotglob nullglob

for pathname in /filepath/orig/v1/*/*; do
    [[ $pathname == *.txt ]] && continue

    printf 'Processing "%s"\n' "$pathname" >&2
    
    cdo info "$pathname" >"$pathname.txt"
done

这首先启用dotglobnullglobshell 选项。这些 shell 选项允许通配模式匹配隐藏名称 ( dotglob) 并确保模式不是匹配的被完全删除( ;这意味着如果不匹配任何名称,nullglob循环将不会运行单次迭代)。/filepath/orig/v1/*/*

循环中已经以 结尾的任何名称都.txt将被跳过,其余的名称将被处理以cdo info生成.txt文件(请注意,我不知道cdo info实际的作用是什么)。请注意,不需要touch先输入文件名,因为将通过重定向到该文件来创建该文件。

有关的:


如果您知道您只会处理名称以 结尾的文件.nc

shopt -s dotglob nullglob

for pathname in /filepath/orig/v1/*/*.nc; do
    printf 'Processing "%s"\n' "$pathname" >&2
    cdo info "$pathname" >"$pathname.txt"
done

如果要处理名称以以下结尾的所有文件.nc 任何地方下面/filepath/orig/v1

find /filepath/orig/v1 -type f -name '*.nc' -exec sh -c '
    for pathname do
        printf "Processing \"%s\"\n" "$pathname" >&2
        cdo info "$pathname" >"$pathname.txt"
    done' sh {} +

这会为批量找到的名称以.nc.

您还可以将/filepath/orig/v1/*/用作搜索路径,find仅搜索子目录/filepath/orig/v1而不搜索/filepath/orig/v1其本身。

答案3

我放弃了“查找”,因为我很难理解它的概念,但似乎这有效 -

orig_dir='/filepath/orig/v1'

for entry in "$orig_dir"/*/*; do
    cd "$entry"
    x=`ls *.nc`
    echo "$x"
    name=`basename $x .nc`
    cdo info "$x" > new_path/"$name".txt
    
done

答案4

如果您使用 GNU 或 BSD find,则可以使用该-execdir选项。它与其他相同,-exec只是它首先更改为包含文件的目录(如果您使用+而不是;终止 -execdir,它会在同一目录中批量处理文件以最小化分叉量每个目录)。例如

find . -type f -execdir \
  sh -c 'for f; do printf "%s\n" "$f" ; cdo info "$f" > "$f.txt"; done' sh {} +

笔记:

  1. for f; do是相同的for f in "$@"; do

  2. 第一的命令的 argsh -c '...'sh.这是将在进程表中使用的名称,以便由or - iesh -c执行。您可以在那里使用任何您喜欢的任意名称 -或者-exec-execdir$0shfind-sh常用的名称。如果不存在,则为 shell 脚本不会看到找到的第一个文件名find。这是特定于sh -c(以及其他一些命令,通常是脚本解释器,如bash -c)的,它是不是对于您可能想要使用find -exec或运行的大多数命令来说是必需的-execdir(例如grepsed不需要它)

  3. 这是-type f因为,即使我们想要查找cd包含文件的目录,我们也只想处理常规文件,而不是目录(或套接字、命名管道、符号链接等)。如果您想处理常规文件和符号链接,请使用 find-L选项或\( -type f -o -type l \).请注意,-L将遵循指向目录的符号链接外部你的搜索树,这通常不是你想要的。

    如果使用\( -type f -o -type l \).嵌入式sh -c脚本应该检查每个参数,以确保它(例如"$f"在我的示例中)是常规文件或指向常规文件的符号链接(test -f将对两者执行此操作,因为如help test和中所述man test,“除了 -h 和-L,所有与 FILE 相关的测试取消引用符号链接。”)。

    find . \( -type f -o -type l \) -execdir \
      sh -c 'for f; do
               printf "%s\n" "$f"
               [ -f "$f" ] && cdo info "$f" > "$f.txt"
             done' sh {} +
    
  4. 脚本中的所有变量扩展sh -c都用双引号引起来。正如它们应该的那样(参见为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?为什么)


如果需要限制搜索深度,可以使用该-maxdepth选项。例如

find . -maxdepth 2 -type f -execdir \
  sh -c 'for f; do printf "%s\n" "$f" ; cdo info "$f" > "$f.txt"; done' sh {} +

find 还具有相关选项,例如-d-depth,以及-mindepth用于控制它如何遍历目录树。


PS:我不知道该cdo命令的作用或它需要什么参数,但如果它支持使用来--标记选项的结尾和文件名参数的开头,则应该将其包含在命令中,否则以 开头的文件名-可能会被视为选项到cdo.例如

find . -type f -execdir \
  sh -c 'for f; do printf "%s\n" "$f" ; cdo info -- "$f" > "$f.txt"; done' sh {} +

printf这是我使用而不是的(部分)原因echo。看为什么 printf 比 echo 更好?

相关内容