如何从管道结果中提取文件名?
How to extract file name from piped results?
我有一个文本文件,文件格式如下:
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 }'
产生:
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 }' | <some basename command here>
期望的输出:
file1
file2
file3
and so on...
使用 basename :) 以下方法适用于我的输入,但可能对带引号的字符串等有问题(感谢 Charles 指出)。
cat mn_s3_files.txt | awk '{print }' | xargs basename
在 Linux 上,您可以使用 -d
和 xargs 来按字面意思处理所有字符。如果您收到 extra operand
错误,请尝试以下操作:
cat foo | awk '{print }' | perl -ne '$_ =~ s[.*/(.*)][]; print "$_";'
perl 版本非常强力,即删除所有内容,直到行中的最后一个 /
。在这种情况下,您也许可以删除 awk。
如果你想一行完成
perl -ne 's[.*/(.*)][]; print "$_"' mn_s3_files.txt
或者您可以使用自动拆分并将分隔符更改为 /
perl -F'/' -ane 'print "$F[4]"' mn_s3_files.txt
读者须知。
此回答的其余部分试图回答评论中 Charles 的问题。请注意,他关于 xargs 和引号的观点是完全有效的,即它们可能会导致问题,但在这种情况下它们并没有给我带来任何问题。
教育学
对于使用命令行的人来说,纯 Bash 解决方案还是使用多个可能的命令和管道的解决方案更具教学意义?我认为这是一个主观问题,没有正确答案。我选择了一种解决方案,即与我在问题中看到的内容密切相关的解决方案,即 OP 理解管道和 cat
所以让我们使用它并在其上构建。我选择不寻求最佳解决方案,因为最佳可能意味着任何事情。我可以在 C/C++ 中编写一个版本,它可以像地狱一样快,但这看起来有点矫枉过正,可能对 OP 没有帮助。
Charles 对这个答案的一些评论让我质疑自己对 *nix 的理解,所以我需要进一步解决这些问题。
Charles 在他的回答中说了以下内容,这让我有些惊讶,强调我的...
You can do this all with only functionality built into bash -- anything like awk or xargs or basename is unnecessary inefficiency.
我决定测试这个,因为我在我工作过的 mac 引擎上没有经历过这个,主要是多核 Mac 和 Linux。我在这里假设效率意味着 运行 脚本需要多长时间,因为如果应用于编写命令行等的时间,那完全取决于使用它的人并且完全是主观的。我对纯 bash 解决方案进行了基准测试,即
#!/bin/bash
while read -r item path; do
name=${path##*/}
printf '%s\n' "$name"
done <mn_s3_files.txt
用时> 17 分钟
real 17m34.959s
user 15m46.912s
sys 1m44.981s
这实际上花费的时间比我想象的要长得多,事实上,在我创建的文件上,我最终两次终止脚本,以为出了点问题,因为我没想到它会这么慢。我仍然不相信事情没有错。 CPU 一直固定在 > 99%。
Charles还提到了以下...
It's much faster for awk to read straight from mn_s3_files.txt than to read from a FIFO that's written to by /bin/cat.
我怀疑在单核 machine 上这可能是真的,但在多核 machine 上它不是 much faster
。请注意,cat
非常高效,并且实际上会将大部分时间花在 IO 上,因为在这种情况下。管道读取端的应用程序读取速度明显慢于 cat
写入速度。我用一堆类似于 OP 的数据创建了一个大文件。
time cat mn_s3_files.txt | awk '{print }' > /dev/null
real 0m59.017s
user 0m57.676s
sys 0m1.833s
与
相比
time awk '{print }' < mn_s3_files.txt > /dev/null
real 0m59.926s
user 0m58.266s
sys 0m1.468s
在这种情况下,首先想到的可能是 fastest
,对于很多人来说就是猫。 运行宁以下命令
time cat mn_s3_files.txt | awk '{print }' | perl -ne '$_ =~ s[.*/(.*)][]; print "$_";' > /dev/null
real 1m6.614s
user 2m2.644s
sys 0m4.221s
cat
在我的 machine 上从未超过 1%
CPU。值得注意的是,awk
和 Perl
在整个过程中都有近 100%
CPU 的使用率,即 效率 。
Charles 提到 start time
是他在讨论 bash 脚本时所指的效率增益...
Re: efficiency -- the benefit of the native while read loop is startup time, not runtime performance with long streams. You'd want to use the bash-native builtins if processing small amounts of data, and an external tool such as awk with very large amounts of data (where the time to start the external tool is overwhelmed by the time spent actually doing the processing).
这对我来说似乎也违反直觉,所以我在小文件上对 bash 与 awk 进行了基准测试。对于只有三行的文件,启动时间对时间没有明显影响,在我的 machine awk 上的多个 运行s 实际上更快 整整毫秒...
time splitter.sh > /dev/null
real 0m0.013s
user 0m0.002s
sys 0m0.006s
awk 时间...
time awk '{gsub(/.*\//, "", ); print }' < 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;
对于非常小的文件,即 < 5 行,这比 awk (节省了高达 11 毫秒)lot faster
lot faster
,但很快就慢了很多,即大约 150 awk 行和 bash 命令行在 13 毫秒处保持水平。因此,要获得 Charles 引用的性能,您需要将其输入命令行而不是将其放入脚本中,否则 运行 脚本的启动成本将 完全破坏性能 :).
地球上最快的打字员
假设您是 fastest typists on the planet
中的一员
世界上最快的打字员在他们最好的情况下每个字母大约 50 毫秒(请注意,我忽略了您可能需要在两个版本中使用大量奇怪字符的事实)。 bash 版本中的字符数约为 90,这意味着如果您以每个字符 50 毫秒的惊人速度打字,则需要约 4 秒。 awk 版本大约有 50 个字符,因此输入大约需要 2.5 秒。
因此,即使您是世界上打字最快的人,awk 版本也比 bash 版本快。
Charles 在另一条评论中说...
I'm not sure cat mn_s3_files.txt | awk '{print }' | xargs basename is ever correct
部分永远正确不正确。我对 xargs 的原始回答和给定的输入字符串适用于以下版本的 mac 10.11.5
使用来自 OP 的输入没有问题。
awk -F'[/.]' '{print }' file
file1
file2
file3
您只需使用内置于 bash 中的功能即可完成这一切——任何类似 awk
或 xargs
或 basename
的东西都是不必要的低效率。
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
显然,要仅发出 item1
和 item2
,请将其设为 printf '%s\n' "$name"
.
我有一个文本文件,文件格式如下:
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 }'
产生:
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 }' | <some basename command here>
期望的输出:
file1
file2
file3
and so on...
使用 basename :) 以下方法适用于我的输入,但可能对带引号的字符串等有问题(感谢 Charles 指出)。
cat mn_s3_files.txt | awk '{print }' | xargs basename
在 Linux 上,您可以使用 -d
和 xargs 来按字面意思处理所有字符。如果您收到 extra operand
错误,请尝试以下操作:
cat foo | awk '{print }' | perl -ne '$_ =~ s[.*/(.*)][]; print "$_";'
perl 版本非常强力,即删除所有内容,直到行中的最后一个 /
。在这种情况下,您也许可以删除 awk。
如果你想一行完成
perl -ne 's[.*/(.*)][]; print "$_"' mn_s3_files.txt
或者您可以使用自动拆分并将分隔符更改为 /
perl -F'/' -ane 'print "$F[4]"' mn_s3_files.txt
读者须知。
此回答的其余部分试图回答评论中 Charles 的问题。请注意,他关于 xargs 和引号的观点是完全有效的,即它们可能会导致问题,但在这种情况下它们并没有给我带来任何问题。
教育学
对于使用命令行的人来说,纯 Bash 解决方案还是使用多个可能的命令和管道的解决方案更具教学意义?我认为这是一个主观问题,没有正确答案。我选择了一种解决方案,即与我在问题中看到的内容密切相关的解决方案,即 OP 理解管道和 cat
所以让我们使用它并在其上构建。我选择不寻求最佳解决方案,因为最佳可能意味着任何事情。我可以在 C/C++ 中编写一个版本,它可以像地狱一样快,但这看起来有点矫枉过正,可能对 OP 没有帮助。
Charles 对这个答案的一些评论让我质疑自己对 *nix 的理解,所以我需要进一步解决这些问题。
Charles 在他的回答中说了以下内容,这让我有些惊讶,强调我的...
You can do this all with only functionality built into bash -- anything like awk or xargs or basename is unnecessary inefficiency.
我决定测试这个,因为我在我工作过的 mac 引擎上没有经历过这个,主要是多核 Mac 和 Linux。我在这里假设效率意味着 运行 脚本需要多长时间,因为如果应用于编写命令行等的时间,那完全取决于使用它的人并且完全是主观的。我对纯 bash 解决方案进行了基准测试,即
#!/bin/bash
while read -r item path; do
name=${path##*/}
printf '%s\n' "$name"
done <mn_s3_files.txt
用时> 17 分钟
real 17m34.959s
user 15m46.912s
sys 1m44.981s
这实际上花费的时间比我想象的要长得多,事实上,在我创建的文件上,我最终两次终止脚本,以为出了点问题,因为我没想到它会这么慢。我仍然不相信事情没有错。 CPU 一直固定在 > 99%。
Charles还提到了以下...
It's much faster for awk to read straight from mn_s3_files.txt than to read from a FIFO that's written to by /bin/cat.
我怀疑在单核 machine 上这可能是真的,但在多核 machine 上它不是 much faster
。请注意,cat
非常高效,并且实际上会将大部分时间花在 IO 上,因为在这种情况下。管道读取端的应用程序读取速度明显慢于 cat
写入速度。我用一堆类似于 OP 的数据创建了一个大文件。
time cat mn_s3_files.txt | awk '{print }' > /dev/null
real 0m59.017s
user 0m57.676s
sys 0m1.833s
与
相比time awk '{print }' < mn_s3_files.txt > /dev/null
real 0m59.926s
user 0m58.266s
sys 0m1.468s
在这种情况下,首先想到的可能是 fastest
,对于很多人来说就是猫。 运行宁以下命令
time cat mn_s3_files.txt | awk '{print }' | perl -ne '$_ =~ s[.*/(.*)][]; print "$_";' > /dev/null
real 1m6.614s
user 2m2.644s
sys 0m4.221s
cat
在我的 machine 上从未超过 1%
CPU。值得注意的是,awk
和 Perl
在整个过程中都有近 100%
CPU 的使用率,即 效率 。
Charles 提到 start time
是他在讨论 bash 脚本时所指的效率增益...
Re: efficiency -- the benefit of the native while read loop is startup time, not runtime performance with long streams. You'd want to use the bash-native builtins if processing small amounts of data, and an external tool such as awk with very large amounts of data (where the time to start the external tool is overwhelmed by the time spent actually doing the processing).
这对我来说似乎也违反直觉,所以我在小文件上对 bash 与 awk 进行了基准测试。对于只有三行的文件,启动时间对时间没有明显影响,在我的 machine awk 上的多个 运行s 实际上更快 整整毫秒...
time splitter.sh > /dev/null
real 0m0.013s
user 0m0.002s
sys 0m0.006s
awk 时间...
time awk '{gsub(/.*\//, "", ); print }' < 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;
对于非常小的文件,即 < 5 行,这比 awk (节省了高达 11 毫秒)lot faster
lot faster
,但很快就慢了很多,即大约 150 awk 行和 bash 命令行在 13 毫秒处保持水平。因此,要获得 Charles 引用的性能,您需要将其输入命令行而不是将其放入脚本中,否则 运行 脚本的启动成本将 完全破坏性能 :).
地球上最快的打字员
假设您是 fastest typists on the planet
中的一员世界上最快的打字员在他们最好的情况下每个字母大约 50 毫秒(请注意,我忽略了您可能需要在两个版本中使用大量奇怪字符的事实)。 bash 版本中的字符数约为 90,这意味着如果您以每个字符 50 毫秒的惊人速度打字,则需要约 4 秒。 awk 版本大约有 50 个字符,因此输入大约需要 2.5 秒。
因此,即使您是世界上打字最快的人,awk 版本也比 bash 版本快。
Charles 在另一条评论中说...
I'm not sure cat mn_s3_files.txt | awk '{print }' | xargs basename is ever correct
部分永远正确不正确。我对 xargs 的原始回答和给定的输入字符串适用于以下版本的 mac 10.11.5
使用来自 OP 的输入没有问题。
awk -F'[/.]' '{print }' file
file1
file2
file3
您只需使用内置于 bash 中的功能即可完成这一切——任何类似 awk
或 xargs
或 basename
的东西都是不必要的低效率。
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
显然,要仅发出 item1
和 item2
,请将其设为 printf '%s\n' "$name"
.