循环遍历文件名列表并迭代 variable/array 从文件名中删除所有字符串 bash
Loop thru a filename list and iterate thru a variable/array removing all strings from filenames with bash
我在变量中有一个字符串列表,想从文件名列表中删除这些字符串。我正在从一个文件中提取该字符串,我可以随着时间的推移添加和修改该文件。变量中的某些字符串可能包含需要删除的项目的一部分,而其他字符串可能是列表中的另一行。这就是为什么我需要遍历整个变量列表的原因。
我熟悉使用 while 循环遍历列表,但不确定如何遍历每一行以从该文件名中删除所有字符串。
这是一个例子:
getstringstoremove=$(cat /text/from/some/file.txt)
echo "$getstringstoremove"
# Or the above can be an array
getstringstoremove=$(cat /text/from/some/file.txt)
declare -a arr=($getstringstoremove)
以上2行应该return以下行
-SOMe.fil
(Ena)M-3_1
.So[Me].filEna)M-3_2
SOMe.fil(Ena)M-3_3
这是我的循环 运行 从目录中获取所有文件名并删除文件名以外的任何内容
ls -l "/files/in/a/folder/" | awk -v N=9 '{sep=""; for (i=N; i<=NF; i++) {printf("%s%s",sep,$i); sep=OFS}; printf("\n")}' | while read line; do
echo "$line"
return每次循环后结果如下
# 1st loop
ilikecoffee1-SOMe.fil(Ena)M-3_1.jpg
# iterate thru $getstringstoremove to remove all strings from the above file.
# 2nd loop
ilikecoffee2.So[Me].filEna)M-3_2.jpg
# iterate thru $getstringstoremove again
# 3rd loop
ilikecoffee3SOMe.fil(Ena)M-3_3.jpg
# iterate thru $getstringstoremove and again
done
最终期望的输出如下
ilikecoffee1.jpg
ilikecoffee2.jpg
ilikecoffee3.jpg
我是 运行 在 bash 上 Mac。
我希望这是有道理的,因为我被困住了并且可以使用一些帮助。
如果有人有更好的方法来做到这一点,那不一定是我上面列出的方法。
我想我已经理解你的意思了,我会用标准内置的 Perl 来完成 macOS
- 所以不需要安装。
我假设您有一个名为 remove.txt
的文件,其中包含要删除的内容列表,并且您想要 运行 当前目录中所有文件的脚本。如果是这样,脚本将是:
#!/usr/local/bin/perl -w
use strict;
# Load the strings to remove into array "strings"
my @strings = `cat remove.txt`;
for(my $i=0;$i<$#strings;$i++){
# Strip carriage returns and quote metacharacters - e.g. *()[]
chomp($strings[$i]);
$strings[$i] = quotemeta($strings[$i]);
}
# Iterate over all filenames
my @files = glob('*');
foreach my $file (@files){
my $new = $file;
# Iterate over replacements
foreach my $string (@strings){
$new =~ s/$string//;
}
# Check if name would change
if($new ne $file){
if( -f $new){
printf("Cowardly refusing to rename %s as %s since it involves overwriting\n",$file,$new);
} else {
printf("Rename %s as %s\n",$file,$new);
# rename $file,$new;
}
}
}
然后将其保存在您的 HOME
目录中作为 renamer
。使其可执行 - 只需要一次 - 在终端中使用此命令:
chmod +x $HOME/renamer
然后你可以进入你疯狂命名的文件所在的任何目录,运行脚本如下:
cd path/to/mad/files
$HOME/renamer
对于您从 Internet 下载的所有内容,请先进行备份,然后只 运行 复制一小部分文件,直到您了解它是如何工作的。
如果你使用 homebrew 作为你的包管理器,你可以安装 rename
使用:
brew install rename
然后你可以从我的另一个答案中提取所有的 Perl 并将其压缩成几行并将其嵌入到 rename
命令中,这会给你带来额外的好处,即能够干 -运行等。下面的代码与我的其他答案完全相同,但对于 non_perl 人来说有点难以阅读。
你的命令很简单:
rename --dry-run '
my @strings = map { s/\r|\n//g; $_=quotemeta($_) } `cat remove.txt`;
foreach my $string (@strings){ s/$string//; } ' *
示例输出
'ilikecoffee(Ena)M-3_1' would be renamed to 'ilikecoffee'
'ilikecoffee-SOMe.fil' would be renamed to 'ilikecoffee'
'ilikecoffee.So[Me].filEna)M-3_2' would be renamed to 'ilikecoffee'
要尝试理解它,请记住:
rename
部分将以下 Perl 应用于每个文件,因为末尾有星号
@strings
部分从文件 remove.txt
中读取所有字符串并从中删除任何回车符 returns 和换行符并引用任何元字符
foreach
将每个删除应用到 rename
为您存储在 $_
中的当前文件名
请注意,此方法在某种程度上以简单性换取了性能。如果你有数百万个文件要做,另一种方法会更快,因为在这里我为每个检查名称的文件读取 remove.txt
文件,但如果你只有几个 hundred/thousand 文件,我怀疑你会注意到它。
这应该大同小异,只是更短:
rename --dry-run '
my @strings = `cat remove.txt`; chomp @strings;
foreach my $string (@strings){ s/\Q$string\E//; } ' *
您可以使用此 awk 单行代码获取新文件名:
$ awk 'NR==FNR{a[[=10=]];next} {for(i in a){n=index([=10=],i);if(n){[=10=]=substr([=10=],0,n-1)substr([=10=],n+length(i))}}} 1' rem.txt files.lst
这假设您的排除字符串在 rem.txt
中并且在 files.lst
中有一个文件列表。
留出空间以便于评论:
NR==FNR { # suck the first file into the indices of an array,
a[[=11=]]
next
}
{
for (i in a) { # for each file we step through the array,
n=index([=11=],i) # search for an occurrence of this string,
if (n) { # and if found,
[=11=]=substr([=11=],0,n-1)substr([=11=],n+length(i))
# rewrite the line with the string missing,
}
}
}
1 # and finally, print the line.
如果您将上述脚本存放在一个文件中,例如 foo.awk
,您可以 运行 将其作为:
$ awk -f foo.awk rem.txt files.lst
查看生成的文件。
请注意,这只是向您展示了如何构建新文件名。如果您想要对目录中的每个文件 do 执行此操作,最好避免 运行 直接从 awk 重命名,并使用为处理文件而设计的 shell 构造,例如 for
循环:
for f in path/to/*.jpg; do
mv -v "$f" "$(awk -f foo.awk rem.txt - <<<"$f")"
done
这应该很明显,除了 awk
选项,它们是:
-f foo.awk
,使用此文件名中的 awk 脚本,
rem.txt
,您的删除字符串列表,
-
,表示除 rem.txt
和 外还应使用标准输入的连字符
<<<"$f"
,"here-string" 将输入提供给 awk。
请注意,此 awk 脚本将适用于 gawk 和 macos 中包含的非 GNU awk。
我在变量中有一个字符串列表,想从文件名列表中删除这些字符串。我正在从一个文件中提取该字符串,我可以随着时间的推移添加和修改该文件。变量中的某些字符串可能包含需要删除的项目的一部分,而其他字符串可能是列表中的另一行。这就是为什么我需要遍历整个变量列表的原因。
我熟悉使用 while 循环遍历列表,但不确定如何遍历每一行以从该文件名中删除所有字符串。
这是一个例子:
getstringstoremove=$(cat /text/from/some/file.txt)
echo "$getstringstoremove"
# Or the above can be an array
getstringstoremove=$(cat /text/from/some/file.txt)
declare -a arr=($getstringstoremove)
以上2行应该return以下行
-SOMe.fil
(Ena)M-3_1
.So[Me].filEna)M-3_2
SOMe.fil(Ena)M-3_3
这是我的循环 运行 从目录中获取所有文件名并删除文件名以外的任何内容
ls -l "/files/in/a/folder/" | awk -v N=9 '{sep=""; for (i=N; i<=NF; i++) {printf("%s%s",sep,$i); sep=OFS}; printf("\n")}' | while read line; do
echo "$line"
return每次循环后结果如下
# 1st loop
ilikecoffee1-SOMe.fil(Ena)M-3_1.jpg
# iterate thru $getstringstoremove to remove all strings from the above file.
# 2nd loop
ilikecoffee2.So[Me].filEna)M-3_2.jpg
# iterate thru $getstringstoremove again
# 3rd loop
ilikecoffee3SOMe.fil(Ena)M-3_3.jpg
# iterate thru $getstringstoremove and again
done
最终期望的输出如下
ilikecoffee1.jpg
ilikecoffee2.jpg
ilikecoffee3.jpg
我是 运行 在 bash 上 Mac。 我希望这是有道理的,因为我被困住了并且可以使用一些帮助。
如果有人有更好的方法来做到这一点,那不一定是我上面列出的方法。
我想我已经理解你的意思了,我会用标准内置的 Perl 来完成 macOS
- 所以不需要安装。
我假设您有一个名为 remove.txt
的文件,其中包含要删除的内容列表,并且您想要 运行 当前目录中所有文件的脚本。如果是这样,脚本将是:
#!/usr/local/bin/perl -w
use strict;
# Load the strings to remove into array "strings"
my @strings = `cat remove.txt`;
for(my $i=0;$i<$#strings;$i++){
# Strip carriage returns and quote metacharacters - e.g. *()[]
chomp($strings[$i]);
$strings[$i] = quotemeta($strings[$i]);
}
# Iterate over all filenames
my @files = glob('*');
foreach my $file (@files){
my $new = $file;
# Iterate over replacements
foreach my $string (@strings){
$new =~ s/$string//;
}
# Check if name would change
if($new ne $file){
if( -f $new){
printf("Cowardly refusing to rename %s as %s since it involves overwriting\n",$file,$new);
} else {
printf("Rename %s as %s\n",$file,$new);
# rename $file,$new;
}
}
}
然后将其保存在您的 HOME
目录中作为 renamer
。使其可执行 - 只需要一次 - 在终端中使用此命令:
chmod +x $HOME/renamer
然后你可以进入你疯狂命名的文件所在的任何目录,运行脚本如下:
cd path/to/mad/files
$HOME/renamer
对于您从 Internet 下载的所有内容,请先进行备份,然后只 运行 复制一小部分文件,直到您了解它是如何工作的。
如果你使用 homebrew 作为你的包管理器,你可以安装 rename
使用:
brew install rename
然后你可以从我的另一个答案中提取所有的 Perl 并将其压缩成几行并将其嵌入到 rename
命令中,这会给你带来额外的好处,即能够干 -运行等。下面的代码与我的其他答案完全相同,但对于 non_perl 人来说有点难以阅读。
你的命令很简单:
rename --dry-run '
my @strings = map { s/\r|\n//g; $_=quotemeta($_) } `cat remove.txt`;
foreach my $string (@strings){ s/$string//; } ' *
示例输出
'ilikecoffee(Ena)M-3_1' would be renamed to 'ilikecoffee'
'ilikecoffee-SOMe.fil' would be renamed to 'ilikecoffee'
'ilikecoffee.So[Me].filEna)M-3_2' would be renamed to 'ilikecoffee'
要尝试理解它,请记住:
rename
部分将以下 Perl 应用于每个文件,因为末尾有星号@strings
部分从文件remove.txt
中读取所有字符串并从中删除任何回车符 returns 和换行符并引用任何元字符foreach
将每个删除应用到rename
为您存储在$_
中的当前文件名
请注意,此方法在某种程度上以简单性换取了性能。如果你有数百万个文件要做,另一种方法会更快,因为在这里我为每个检查名称的文件读取 remove.txt
文件,但如果你只有几个 hundred/thousand 文件,我怀疑你会注意到它。
这应该大同小异,只是更短:
rename --dry-run '
my @strings = `cat remove.txt`; chomp @strings;
foreach my $string (@strings){ s/\Q$string\E//; } ' *
您可以使用此 awk 单行代码获取新文件名:
$ awk 'NR==FNR{a[[=10=]];next} {for(i in a){n=index([=10=],i);if(n){[=10=]=substr([=10=],0,n-1)substr([=10=],n+length(i))}}} 1' rem.txt files.lst
这假设您的排除字符串在 rem.txt
中并且在 files.lst
中有一个文件列表。
留出空间以便于评论:
NR==FNR { # suck the first file into the indices of an array,
a[[=11=]]
next
}
{
for (i in a) { # for each file we step through the array,
n=index([=11=],i) # search for an occurrence of this string,
if (n) { # and if found,
[=11=]=substr([=11=],0,n-1)substr([=11=],n+length(i))
# rewrite the line with the string missing,
}
}
}
1 # and finally, print the line.
如果您将上述脚本存放在一个文件中,例如 foo.awk
,您可以 运行 将其作为:
$ awk -f foo.awk rem.txt files.lst
查看生成的文件。
请注意,这只是向您展示了如何构建新文件名。如果您想要对目录中的每个文件 do 执行此操作,最好避免 运行 直接从 awk 重命名,并使用为处理文件而设计的 shell 构造,例如 for
循环:
for f in path/to/*.jpg; do
mv -v "$f" "$(awk -f foo.awk rem.txt - <<<"$f")"
done
这应该很明显,除了 awk
选项,它们是:
-f foo.awk
,使用此文件名中的 awk 脚本,rem.txt
,您的删除字符串列表,-
,表示除rem.txt
和 外还应使用标准输入的连字符
<<<"$f"
,"here-string" 将输入提供给 awk。
请注意,此 awk 脚本将适用于 gawk 和 macos 中包含的非 GNU awk。