【问题标题】:How to loop over files in directory and change path and add suffix to filename如何遍历目录中的文件并更改路径并将后缀添加到文件名
【发布时间】:2014-01-14 18:43:48
【问题描述】:

我需要编写一个脚本,用不同的参数启动我的程序,但我是 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

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

【问题讨论】:

  • @LéaGris 提议的副本似乎不那么出色,尤其是作为其中的一个答案仍然提倡循环 ls 输出。这些看起来很不一样,我也没有提名它作为这个的副本。

标签: bash for-loop filenames glob


【解决方案1】:

首先要注意几点:当您使用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

注意事项:

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

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

【讨论】:

  • 如果没有找到文件/匹配通配符,我发现 for 循环执行块仍然输入一次,文件名 =“/Data/*.txt”。我怎样才能避免这种情况?
  • @OliverPearmain 在循环之前使用shopt -s nullglob(在之后使用shopt -u nullglob以避免以后出现问题),或者在循环的开头添加if [[ ! -e "$filename ]]; then continue; fi,这样它就会跳过不存在的文件.
  • 完美shopt -s nullglob 效果很好。感谢您提供信息和及时回复。
  • 当文件名中包含空格时,这不起作用。
  • @Isa 它应该与空格一起使用,只要所有双引号都到位。去掉任何双引号,你就会遇到空格问题。
【解决方案2】:

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

例如:

for filename in Data/*.txt; do
    [ -e "$filename" ] || continue
    # ... rest of the loop body
done

参考:Bash Pitfalls

【讨论】:

  • 这仍然是一个及时的警告。我以为我错误地创建了我的脚本,但我的文件扩展名是小写而不是大写,它没有找到文件,并返回 glob 模式。呃。
  • 由于使用了 Bash 标签:这就是 shopt nullglob 的用途! (或shopt failglob 也可以使用,具体取决于您想要的行为)。
  • 此外,这还会在循环到达之前检查处理过程中删除的文件。
  • 还可以处理您有一个名为dir.txt的目录的情况
  • 别抱歉; Stack Overflow 的全部目的是收集和整理规范问题,而不是让新用户重新提出相同的旧问题。
【解决方案3】:
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

name=${file##*/} 替换 (shell parameter expansion) 会删除前导路径名,直到最后一个 /

base=${name%.txt} 替换删除了结尾的 .txt。如果扩展名可以变化,那就有点棘手了。

【讨论】:

  • 我认为您的代码有错误。一行应该是base=${name%.txt},而不是base=${base%.txt}
  • @CaseyKlimkowsky:是的;当代码和 cmets 不一致时,至少其中一个是错误的。在这种情况下,我认为只有一个——代码;通常,实际上两者都是错误的。感谢您指出了这一点;我已经修好了。
  • 你可以使用name=${file##*/}而不是name=`basename $file`
【解决方案4】:

您可以使用 finds null 分隔输出选项和 read 来安全地遍历目录结构。

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

所以对于你的情况

#!/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

另外

#!/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)

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

【讨论】:

  • $'\0' 是一种奇怪的写法''。您缺少IFS=-r 切换到read:您的阅读语句应该是:IFS= read -rd '' file
  • 我认为有些人需要搜索 $'\0' 并散布一些堆栈点。将进行您指出的编辑。没有IFS= 尝试echo -e "ok \nok\0" | while read -d '' line; do echo -e "$line"; done 有什么不良影响似乎没有。此外,我看到的 -r 通常是默认值,但找不到它阻止发生的示例。
  • 如果文件名以空格结尾,则需要 IFS=:try 以 touch 'Prospero ' 结尾(注意尾随空格)。如果文件名有反斜杠,您还需要-r 开关:尝试使用touch 'Prospero\n'
  • 被低估的答案。在包含超过 100k 个文件的目录中尝试最受好评的那些。如果您使用的是低端机器,祝您好运。
【解决方案5】:

看起来您正在尝试执行 Windows 文件 (.exe) 当然您应该使用 powershell。无论如何,在 Linux bash shell 上,一个简单的单行就足够了。

[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done

或者在 bash 中

#!/bin/bash

for filename in /Data/*.txt; 
   do
     for i in {0..3}; 
       do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt; 
     done 
 done

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 2021-01-21
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多