如何使用Bash迭代目录中的文件?

Dob*_*obr 515 bash filenames for-loop glob

我需要编写一个脚本,用不同的参数启动我的程序,但我是Bash的新手.我这样开始我的程序:

./MyProgram.exe Data/data1.txt [Logs/data1_Log.txt].

这是我想要做的伪代码:

for each filename in /Data do
  for int i = 0, i = 3, i++
    ./MyProgram.exe Data/filename.txt Logs/filename_Log{i}.txt
  end for
end for
Run Code Online (Sandbox Code Playgroud)

所以我真的很困惑如何从第一个创建第二个参数,所以它看起来像dataABCD_Log1.txt并启动我的程序.非常感谢帮助.

PS我知道那里有类似的问题,但我没有发现创建我的日志文件名称.

Gor*_*son 682

首先要注意几个注意事项:当你使用它Data/data1.txt作为参数时,它应该是真的/Data/data1.txt吗(带有一个前导斜杠)?此外,外部循环是否应仅扫描.txt文件或/ Data中的所有文件?这是一个答案,/Data/data1.txt仅假设和.txt文件:

#!/bin/bash
for filename in /Data/*.txt; do
    for ((i=0; i<=3; i++)); do
        ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt"
    done
done
Run Code Online (Sandbox Code Playgroud)

笔记:

  • /Data/*.txt扩展到/ Data中的文本文件的路径(包括/Data/part)
  • $( ... ) 运行shell命令并在命令行中的该点插入其输出
  • basename somepath .txt输出somepath的基本部分,从末尾删除.txt(例如/Data/file.txt- > file)

如果您需要使用Data/file.txt而不是运行MyProgram /Data/file.txt,请使用"${filename#/}"删除前导斜杠.在另一方面,如果它真的Data不是/Data要扫描,只需使用for filename in Data/*.txt.

  • 如果没有找到/匹配通配符的文件我发现for循环执行块仍然输入一次filename ="/ Data/*.txt".我怎么能避免这个? (15认同)
  • @OliverPearmain在循环之前使用`shopt -s nullglob`(以及之后的`shopt -u nullglob`以避免以后出现问题),或者添加`if [[!-e"$ filename]];然后在循环开始时继续; fi`,这样它就会跳过不存在的文件. (15认同)
  • 当存在名称中包含空格的文件时,这不起作用. (9认同)
  • @Isa只要所有的双引号都到位,它就应该与空白一起使用。忽略任何双引号,您将遇到空格问题。 (4认同)
  • 完美的 `shopt -s nullglob` 效果很好。感谢您的信息和及时回复。 (2认同)

Con*_* Ma 310

很抱歉对线程进行了解析,但是每当你通过globbing迭代文件时,最好避免使用glob不匹配的极端情况(这会使循环变量扩展为(不匹配)glob模式字符串本身).

例如:

for filename in Data/*.txt; do
    [ -e "$filename" ] || continue
    # ... rest of the loop body
done
Run Code Online (Sandbox Code Playgroud)

参考:Bash陷阱

  • Stack Overflow意味着永远不必为死灵道歉! (153认同)
  • 这仍是一个及时的警告.我以为我错误地创建了我的脚本,但我的文件扩展名是小写而不是大写,它没有找到文件,并返回了glob模式.啊. (8认同)
  • 由于使用了Bash标签:这就是[`shopt nullglob`](https://www.gnu.org/software/bash/manual/bashref.html#The-Shopt-Builtin)的用途!(或者`shopt failglob`也可以使用,具体取决于你想要的行为). (4认同)
  • 还处理您有一个名为“dir.txt”的目录的情况 (2认同)
  • 别后悔;Stack Overflow 的全部目的是收集和整理规范问题,而不是让新用户重复相同的旧问题。 (2认同)

Jon*_*ler 71

for file in Data/*.txt
do
    for ((i = 0; i < 3; i++))
    do
        name=${file##*/}
        base=${name%.txt}
        ./MyProgram.exe "$file" Logs/"${base}_Log$i.txt"
    done
done
Run Code Online (Sandbox Code Playgroud)

name=${file##*/}取代(壳参数扩展)删除前导路径名直到最后/.

所述base=${name%.txt}取代去除拖尾.txt.如果扩展可以变化,这有点棘手.

  • @CaseyKlimkowsky:是的; 当代码和评论不一致时,其中至少有一个是错误的.在这种情况下,我认为它只是一个 - 代码; 通常,这实际上都是错误的.感谢您指出了这一点; 我修好了. (3认同)
  • 我相信您的代码中有错误。一行应该是“ base = $ {name%.txt}”,而不是“ base = $ {base%.txt}”。 (2认同)

Seb*_*icz 14

对每个文件运行命令:

对所有.txt文件执行某些操作(回显),

for f in *.txt; do echo ${f}; done;
Run Code Online (Sandbox Code Playgroud)


Mag*_*ero 5

您可以将finds null分隔的输出选项与read一起使用,以安全地遍历目录结构。

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'\0' file; 
  do echo "$file" ;
done
Run Code Online (Sandbox Code Playgroud)

所以对于你的情况

#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done
Run Code Online (Sandbox Code Playgroud)

另外

#!/bin/bash
while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done < <(find . -maxdepth 1 -type f  -print0)
Run Code Online (Sandbox Code Playgroud)

将在脚本(流程)的当前范围内运行while循环,并在需要时允许将find的输出用于设置变量

  • $'\ 0'是一种怪异的书写方式。您缺少`IFS =`,并且`-r`切换为`read`:您的read语句应为:'IFS = read -rd” file`。 (2认同)
  • 如果文件名以空格结尾,则需要`IFS =`:尝试使用`touch'Prospero'`(注意尾随空格)。另外,如果文件名带有反斜杠,则需要使用-r开关:请尝试触摸“ Prospero \ n”。 (2认同)
  • 被低估的答案。在包含超过 100k 文件的目录中尝试最受支持的目录。如果您使用的是低端机器,祝您好运。 (2认同)