如何从管道结果中提取文件名?

时间:2016-05-19 23:17:23

标签: linux bash pipe filenames

我有一个包含以下格式的文件的文本文件:

item1 a/b/c/d/file1.csv
item2 a/b/c/d/file2.csv
item3 a/b/c/d/file3.csv
and so on...

要隔离每行中的第二项,我使用以下内容:

cat mn_s3_files.txt | awk '{ print $1 }'

哪个收益率:

a/b/c/d/file1.csv
a/b/c/d/file2.csv
a/b/c/d/file3.csv
and so on...

现在,我如何只从管道结果中提取基本名称?

例如:

cat mn_s3_files.txt | awk '{ print $1 }' | <some basename command here>

期望的输出:

file1
file2
file3
and so on...

3 个答案:

答案 0 :(得分:1)

使用basename :)以下方法对我的输入有效,但可能会遇到引用字符串等问题(感谢Charles指出)。

cat mn_s3_files.txt  | awk '{print $2}' | xargs basename

在Linux上,你可以使用-d和xargs来逐字处理所有字符。如果您收到extra operand错误,请尝试以下操作:

cat foo | awk '{print $2}' | perl -ne '$_ =~ s[.*/(.*)][$1]; print "$_";'

perl版本非常强大,即将所有内容剥离到行的最后/。在这种情况下你可以删除awk。

如果你想在一行中完成

perl -ne 's[.*/(.*)][$1]; print "$_"'  mn_s3_files.txt

或者您可以使用自动拆分并将分隔符更改为/

perl -F'/' -ane 'print "$F[4]"'  mn_s3_files.txt

读者注意。

本答案的其余部分是试图在评论中回答Charles中的问题。请注意,他关于xargs和引号的观点是完全有效的,即它们可能会导致问题,在这种情况下它们只是没有引起任何问题。

教学法

对于使用命令行,纯Bash解决方案或使用多个可能命令和管道的解决方案的人来说,更具教学意义的是什么?我认为这是一个主观问题,并没有正确的答案。我选择了一个解决方案,即一个与我在问题中看到的密切相关的解决方案,即OP了解管道和cat所以让我们使用它并在其上构建。我选择不去寻求最佳解决方案,因为最佳可能意味着什么。我可以用C / C ++编写一个版本,它可以像地狱一样快,但这看起来有点矫枉过正,可能无法帮助OP。

查尔斯对这个答案的一些评论让我质疑我对* nix的理解,所以我需要进一步解决这些问题。

Charles在他的回答中说明以下内容让我感到有些惊讶,强调我的......

  

你可以只使用bash内置的功能来实现这一切 - 像awk或xargs或basename之类的东西不必要的低效率

我决定对此进行测试,因为我在我曾经使用的机器上没有经历过这种情况,主要是多核Mac和Linux。我在这里假设效率意味着运行脚本需要多长时间,因为如果应用于编写命令行等多长时间,它完全依赖于人类使用它并且完全是主观的。我对纯粹的bash解决方案进行了基准测试,即

#!/bin/bash
while read -r item path; do
  name=${path##*/}
  printf '%s\n' "$name"
done <mn_s3_files.txt

所花费的时间是&gt; 17分钟

real    17m34.959s
user    15m46.912s
sys     1m44.981s

这实际上花了比我想象的更长的时间,事实上在我创建的文件中我最终杀死了两次脚本,认为出了问题,因为我并不期待它变慢。我还是不相信有什么不对的。 CPU与&gt;挂钩全程99%。

Charles还提到了以下内容......

  

直接从mn_s3_files.txt读取awk比从/ bin / cat写入的FIFO中读取要快得多。

我怀疑在一台核心机器上这可能是真的,但在多核机器上它不是much faster。请注意,cat非常有效,并且会在IO上花费大部分时间,因为在这种情况下。管道读取端的应用程序在读取时明显慢于写入时cat。我创建了一个包含大量类似于OP的数据的大文件。

time cat mn_s3_files.txt  | awk '{print $2}' > /dev/null 

real    0m59.017s
user    0m57.676s
sys     0m1.833s

相比
time awk '{print $2}' < mn_s3_files.txt > /dev/null

real    0m59.926s
user    0m58.266s
sys     0m1.468s

在这种情况下,首先想到的可能是fastest和许多人的猫。运行以下命令时

time cat mn_s3_files.txt  | awk '{print $2}' | perl -ne '$_ =~ s[.*/(.*)][$1]; print "$_";' > /dev/null

real    1m6.614s
user    2m2.644s
sys 0m4.221s

cat从未在我的计算机上达到1%以上的CPU。值得注意的是,虽然awkPerl的CPU使用率几乎都是100%,但它的效率要低得多

Charles提到start time是他在讨论bash脚本时的效率提升......

  

Re:效率 - 本地读取循环的好处是启动时间,而不是长流的运行时性能。您希望在处理少量数据时使用bash-native内置函数,以及使用具有大量数据的外部工具(如awk)(其中启动外部工具的时间被实际执行的时间所淹没)处理)。

这对我来说似乎违反直觉,所以我在小文件上对bash vs awk进行了基准测试。对于只有三行的文件,启动时间对时间没有明显的影响,在我的机器上多次运行awk实际上更快一整毫秒 ......

time splitter.sh > /dev/null

real    0m0.013s
user    0m0.002s
sys     0m0.006s

awk的时间......

time awk '{gsub(/.*\//, "", $2); print $2}' < mn_s3_files2.txt > /dev/null

real    0m0.013s
user    0m0.002s
sys     0m0.006s

我也是在一个空文件上做的,awk更快。请注意,此时我意识到Charles正在谈论在命令行输入它,所以我尝试了,即

time while read -r item path; do name=${path##*/}; printf '%s\n' "$name"; done <mn_s3_files2.txt;

对于非常小的文件,这是lot faster比awk (节省了大约11ms),即&lt; 5条线,但速度非常快,即大约150线awk和bash命令线在13ms的水平盯住。因此,要获得Charles引用的性能,您需要将其键入命令行而不是将其放入脚本中,否则运行脚本的启动成本将完全消除性能:)。

这个星球上最快的打字员

让我们假设你是fastest typists on the planet

中的一个

世界上打字速度最快的打字员每个字母的最佳播放时间大约为50毫秒(请注意,我忽略了您可能需要在两个版本中使用大量奇数字符的事实)。 bash版本中的字符数大约为90,这意味着如果你以每个字符50ms的惊人速度输入,则需要大约4秒。 awk版本大约有50个字符,因此输入大约需要2.5秒。

所以,即使你是世界上最快的打字员,awk版本也比bash版本更快。

Charles在另一条评论中说......

  

我不确定cat mn_s3_files.txt | awk&#39; {print $ 2}&#39; | xargs basename永远是正确的

部分永远正确不正确。我对xargs的原始答案和给定的输入字符串在以下版本的mac 10.11.5上使用OP的输入而没有任何问题。

答案 1 :(得分:0)

awk -F'[/.]' '{print $5}' file
file1
file2
file3

答案 2 :(得分:0)

您只需使用bash中内置的功能即可完成所有操作 - awkxargsbasename等任何内容都是不必要的低效率。

while read -r item path; do
  name=${path##*/}
  printf 'Read %q from %q\n' "$item" "$name"
done <mn_s3_files.txt

...得到以下特性:

read item1 from file1.csv
read item2 from file2.csv

显然,要仅发出item1item2,请将其设为printf '%s\n' "$name"