我之前曾在
上提出过类似的问题Extracting a list of files and creating a new file containing this list
然而,这一次更具挑战性:
我目前正在处理一个包含大约1000个文件的文件夹,我必须从该文件夹中提取一些文件名,然后创建另一个包含这些文件名的文件(配置文件)。
基本上,该文件夹的文件名格式如下:
1_Apple_A1_someword.txt
1_Apple_A2_someword.txt
2_Apple_A1_someword.txt
2_Apple_A2_someword.txt
3_Apple_A1_someword.txt
3_Apple_A2_someword.txt
等等直到
1000_Apple_A1_someword.txt
1000_Apple_A2_someword.txt
我想创建另一个文件,其中包含每个文件的'标签'(Unix变量),其值是下面格式中每个“标签”的两个文件的名称。 (每个标签的两个文件由选项卡分隔)。此外,'标签'是文件名的一部分(直到单词“Apple”之前的所有内容)例如,
1_Apple=1_Apple_A1_someword.txt 1_Apple_A2_someword.txt
2_Apple=2_Apple_A1_someword.txt 2_Apple_A2_someword.txt
3_Apple=3_Apple_A1_someword.txt 3_Apple_A2_someword.txt
依此类推......直到
1000_Apple=1000_Apple_A1_someword.txt 1000_Apple_A2_someword.txt
你能告诉我一个单行的Unix命令吗?也许使用“awk”和“sed”
答案 0 :(得分:1)
使用sed脚本:
#!/bin/sed -nf
: loop
H
s/\([^_]*_[^_]*\)_.*/\1/g
t clear_flag
: clear_flag
$! {
N
s/^\([^_]*_[^\n]*\)\n\(\1[^\n]*\)$/\2/
t loop
}
x
s/^\n//
s/\([^_]*_[^_]*\)_/\1=\1_/
s/\n/ /gp
s/.*//
x
D
我会尝试解释一切。首先,我们有一个循环将所有以相同前缀开头的文件连接在一起。我根据您的示例定义了一个前缀,并将其定义为以第二个下划线结尾的字符串。循环由标签定义,使用":"命令。在这里,我们将循环标记为" loop"。在下面,必要时,我们跳过"使用" t"回到循环开始测试命令。
第一个命令是将该行附加到保持空间(辅助缓冲区)。在添加之前,该行会自动以sed为前缀(' \ n')。
第二个命令提取前缀。我们通过捕获一系列不是下划线([^_]*
)的字符,然后是下划线,然后是更多没有下划线的字符来实现这一点。因为此模式位于反斜杠括号(\(
和\)
之间),所以sed将捕获与此模式匹配的输入并保存到名为\1
的辅助变量中(因为它是第一次捕获那条线)。然后我们跳过一个下划线,然后是一系列任何字符。替换是我们捕获的,所以实际上我们只删除了包括第二个下划线之后的所有内容。
我们现在使用一种解决方法来清除seds内部标志,指示自上次" t"以来是否发生了成功的替换。命令或自脚本启动以来。如果替换命令成功,则测试命令(" t")将分支(跳转)到标签,然后清除内部标志。这对我们的第二个" t"进一步命令如下。如果它成功或失败(即,如果它是否分支),它仍然会在" clear_flag"之后继续执行。标签
现在我们使用" {"命令启动一组命令。但是,我们之前有一个地址前缀,sed用它来确定它是否应该运行这些命令。在我们的例子中,只有在读取的最后一行输入行不是最后一行时才会执行该组(美元符号" $"表示最后一行输入行,"!& #34;代表否定)。
组中的第一个命令会将输入的下一行追加到当前模式空间(即工作缓冲区)。上一行和新行由换行符(\n
)分隔。
第三个命令将检查新读取的行是否以我们的前缀开头,并删除隔离的前缀(即前一行)。因为我们从前一行保留的前缀中删除了第二个下划线,并且因为我们附加了一个新行,所以隔离前缀现在在换行符之前结束。因此,捕获的模式现在读取在下划线之后不是换行符([^\n]*
)的字符。在我们捕获了孤立的前缀之后,我们跳过分隔前一行和新行的换行符,然后我们开始另一次捕获(将存储在\2
中,因为它是此行的第二次捕获)。此捕获将(希望)与第二行匹配。希望因为我们要求匹配开始时与第一次捕获中的匹配完全相同(第二次捕获中的第一件事是对第一次捕获的反向引用,即。\1
)。之后,我们匹配一系列不是新行的字符,在第二次捕获之后,我们期望该行的结束。
如果最后一个替换命令成功,我们发现新读取的行也有相同的前缀,所以我们现在必须跳回到循环的开头。这是" t"的功能。命令。它将测试自上次" t"以来是否有任何替换命令成功。命令,如果是,则转移到给定标签。在我们的例子中,我们分支(跳转)回到"循环"标签。现在我们可以看到为什么我们需要以前的" t"解决方法。没有它,第一个替代命令可能会成功,而我们真正感兴趣的命令可能会失败,并且" t"仍然会回到"循环"标签
如果它离开循环,则意味着新读取的行不具有相同的前缀。因此,我们现在可以打印之前匹配的内容。
我们首先使用交换(" x")命令交换模式空间的内容和保留空间的内容。现在我们的模式空间包含具有相同前缀的所有文件,并且我们的保留空间包含隔离行中的当前前缀,然后是包含第一个不共享相同前缀的文件的行。
由于之前我们将所有文件名附加到保留空间,因此所有文件名都由换行符分隔,并且由于还附加了第一个文件名,因此当前模式空间中的第一个字节是换行符。要删除它,我们只需简单地替换它。
现在我们必须生成赋值的格式。这就是为什么我们有一个熟悉的替换命令,我们再次提取前缀,除了现在我们已删除.*
以保持线的其余部分完好无损。替换包括前缀(捕获),等号,我们还恢复从模式空间中的第一个文件中删除的内容:它的前缀和下划线。
我们几乎已准备好打印出该行,但文件名仍由换行符分隔。因此,我们用空格替换所有换行符(g
标志告诉sed在输入行上尽可能多地重复替换命令)。从现在开始就行了,我们可以添加p
前缀来告诉sed打印它。
最后一步是准备再次启动脚本,以获取下一个前缀。保留空间必须为空,以便可以用于存储具有新前缀的文件名。我们有一个命令用什么都不替换模式空间中的每个字符,然后是交换命令。
保留空间已准备就绪。现在我们必须准备模式空间。它必须只包含带有新前缀的文件名的第一行。要处于该状态,我们所要做的就是删除存储在第一行中的旧前缀。我们可以执行类似s/.*\n//
的操作来替换除最后一行(包含带有新前缀的文件名)的字符之外的所有字符,但D
命令将执行此操作并强制脚本执行在没有读取另一行的情况下再次开始执行,因此它为我们节省了一些输入。
虽然剧本可能有点神秘并且描述势不可挡,但一旦你理解了会发生什么,它就会变得简单(r)=)
必须提及的内容:必须对输入进行排序(或者至少必须将具有相同前缀的文件组合在一起)。
希望这有帮助!
答案 1 :(得分:1)
这可能适合你(GNU sed):
sed '$!N;s/^\(\(.*\)_.*_.*\)\n/\2=\1 /' file
答案 2 :(得分:1)
> ls -1 | perl -F_ -ane 'chomp;if($_=~m/Apple_A/){$X{$F[0]."_".$F[1]}=$X{$F[0]."_".$F[1]}." ".$_;}END{foreach (keys %X){print $_."=".$X{$_}."\n"}}'
3_Apple= 3_Apple_A1_someword.txt 3_Apple_A2_someword.txt
2_Apple= 2_Apple_A1_someword.txt 2_Apple_A2_someword.txt
1_Apple= 1_Apple_A1_someword.txt 1_Apple_A2_someword.txt
答案 3 :(得分:0)
使用Perl:
perl -pe 'if ($. % 2) { /([0-9]+_Apple)/ and print "$1="; s/\s+$/ /; }'
在奇数行上,匹配... Apple,用=输出,并用一个空格替换行尾的空格。
注意: Unix变量的名称不能以数字开头。
答案 4 :(得分:0)
使用短awk单行:
awk -F'_' '{if (NR % 2) {printf("%s_%s=%s", $1, $2, $0)} else {print}}' FILE
答案 5 :(得分:0)
使用sed:
sed 'N;s/\n/ /;s/\([^_]*_Apple\)/\1=\1/'
答案 6 :(得分:0)
num=1
while [ $num -le 1000 ]
do
echo "${num}_Apple=${num}_Apple_A1_someword.txt ${num}_Apple_A2_somword.txt"
num=`expr $num + 1`
done
输出:
1_Apple=1_Apple_A1_someword.txt 1_Apple_A2_somword.txt
2_Apple=2_Apple_A1_someword.txt 2_Apple_A2_somword.txt
3_Apple=3_Apple_A1_someword.txt 3_Apple_A2_somword.txt
4_Apple=4_Apple_A1_someword.txt 4_Apple_A2_somword.txt
5_Apple=5_Apple_A1_someword.txt 5_Apple_A2_somword.txt
...........
如果数字1000不是静态的,那么您可以从文件本身获取值:
num=`cat file|sort|tail -1|awk -F"_" '{print $1}'
由于