我试图循环遍历文件夹以获取文件并对它们执行某些操作,将输出重定向到与文件同名的文本文件。我尝试使用“查找”-
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
这首先启用dotglob
和nullglob
shell 选项。这些 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 {} +
笔记:
for f; do
是相同的for f in "$@"; do
这第一的命令的 arg
sh -c '...'
是sh
.这是将在进程表中使用的名称,以便由or - iesh -c
执行。您可以在那里使用任何您喜欢的任意名称 -或者-exec
-execdir
$0
sh
find-sh
常用的名称。如果不存在,则为 shell 脚本不会看到找到的第一个文件名find
。这是特定于sh -c
(以及其他一些命令,通常是脚本解释器,如bash -c
)的,它是不是对于您可能想要使用find -exec
或运行的大多数命令来说是必需的-execdir
(例如grep
,sed
不需要它)这是
-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 {} +
脚本中的所有变量扩展
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 更好?