bash 如何遍历目录中的文件并更改路径并向文件名添加后缀

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20796200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:34:19  来源:igfitidea点击:

How to loop over files in directory and change path and add suffix to filename

bashfor-loopfilenamesglob

提问by Dobrobobr

I need to write a script that starts my program with different arguments, but I'm new to Bash. I start my program with:

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

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

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

Here is the pseudocode for what I want to do:

这是我想要做的伪代码:

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

So I'm really puzzled how to create second argument from the first one, so it looks like dataABCD_Log1.txt and start my program.

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

回答by Gordon Davisson

A couple of notes first: when you use Data/data1.txtas an argument, should it really be /Data/data1.txt(with a leading slash)? Also, should the outer loop scan only for .txt files, or all files in /Data? Here's an answer, assuming /Data/data1.txtand .txt files only:

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

Notes:

笔记:

  • /Data/*.txtexpands to the paths of the text files in /Data (includingthe /Data/ part)
  • $( ... )runs a shell command and inserts its output at that point in the command line
  • basename somepath .txtoutputs the base part of somepath, with .txt removed from the end (e.g. /Data/file.txt-> file)
  • /Data/*.txt扩展到 /Data 中的文本文件的路径(包括/Data/ 部分)
  • $( ... )运行一个 shell 命令并在命令行中的那个点插入它的输出
  • basename somepath .txt输出 somepath 的基本部分,从末尾删除 .txt (例如/Data/file.txt-> file

If you needed to run MyProgram with Data/file.txtinstead of /Data/file.txt, use "${filename#/}"to remove the leading slash. On the other hand, if it's really Datanot /Datayou want to scan, just use for filename in Data/*.txt.

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

回答by Cong Ma

Sorry for necromancing the thread, but whenever you iterate over files by globbing, it's good practice to avoid the corner case where the glob does not match (which makes the loop variable expand to the (un-matching) glob pattern string itself).

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

For example:

例如:

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

Reference: Bash Pitfalls

参考:Bash 陷阱

回答by Jonathan Leffler

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

The name=${file##*/}substitution (shell parameter expansion) removes the leading pathname up to the last /.

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

The base=${name%.txt}substitution removes the trailing .txt. It's a bit trickier if the extensions can vary.

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

回答by James

You can use finds null separated output option with read to iterate over directory structures safely.

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

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'
#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'
#!/bin/bash
while IFS= read -r -d $'
[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done
' 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)
' file; do for ((i=0; i<=3; i++)); do ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt' done done
' file; do echo "$file" ; done

So for your case

所以对于你的情况

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

additionally

此外

##代码##

will run the while loop in the current scope of the script ( process ) and allow the output of find to be used in setting variables if needed

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

回答by user3183111

Looks like you're trying to execute a windows file (.exe) Surely you ought to be using powershell. Anyway on a Linux bash shell a simple one-liner will suffice.

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

##代码##

Or in a bash

或者在 bash 中

##代码##