我在变量中有一个字符串列表,并希望从文件名列表中删除这些字符串。我从一个文件中提取该字符串,我可以添加并随时修改。变量中的一些字符串可能包括需要删除的项目的一部分,而另一个可能是列表中的另一行。这就是为什么我需要循环通过整个变量列表。
我很熟悉使用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应返回以下行
-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"
每次循环后返回以下结果
# 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
我在Mac上用bash运行它。 我希望这是有道理的,因为我被困住了,可以使用一些帮助。
如果有人通过各种方式更好地做到这一点,那就不一定是我上面列出的方式了。
答案 0 :(得分:2)
您可以使用此awk one-liner获取新文件名:
$ awk 'NR==FNR{a[$0];next} {for(i in a){n=index($0,i);if(n){$0=substr($0,0,n-1)substr($0,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[$0]
next
}
{
for (i in a) { # for each file we step through the array,
n=index($0,i) # search for an occurrence of this string,
if (n) { # and if found,
$0=substr($0,0,n-1)substr($0,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
查看生成的文件。
请注意,这只是向您展示如何构建新的文件名。如果你想要的是 做 这对于目录中的每个文件,最好避免直接从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"
,&#34; here-string&#34;向awk提供输入。请注意,这个awk脚本可以同时使用gawk和macos中包含的非GNU awk。
答案 1 :(得分:1)
我想我已经理解了你的意思,我会用内置于标准macOS
的Perl来做 - 所以无需安装。
我假设您有一个名为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
与从互联网上下载的所有内容一样,首先进行备份,然后运行文件的小型复制子集,直到您了解其工作原理为止。
答案 2 :(得分:1)
如果您使用自制软件作为软件包管理器,则可以使用以下命令安装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
中的所有字符串,并从中删除任何回车符和换行符并引用任何元字符foreach
将每个删除应用于rename
为$_
存储的当前文件名请注意,此方法在某种程度上简化了性能。如果你有数百万个文件要做,另一种方法会更快,因为在这里我为每个检查了名称的文件读了remove.txt
文件,但如果你只有几百/千个文件,我怀疑你会注意到的。
这应该是相同的,只是更短:
rename --dry-run '
my @strings = `cat remove.txt`; chomp @strings;
foreach my $string (@strings){ s/\Q$string\E//; } ' *