How to find the last field using 'cut'
如果不使用sed或awk,而仅使用cut,那么当字段数未知或每行更改时,如何获取最后一个字段?
-
你不能。为什么必须使用cut?
-
您是否爱上cut命令:)?为什么没有其他Linux命令?
-
如果没有sed或awk:perl -pe s^.+\s+([^\s]+)$$1。
-
@jaynesh如zfus正确猜中,是的,这是家庭作业,我们不能使用sed或awk。如果我有几行简单的文本,其中每一行都是一个网址,则有一个定界符。我需要从那些网址中提取最后一个字段,即com,net,nz。但是数量。 (定界符)始终随每个地址而变化,但始终是最后一个字段。我认为削减是显而易见的选择:
-
如果将问题仅限于期望的答案(在本例中为cut),则您将无法学习任何新知识。
-
如何在shell中拆分字符串并获取最后一个字段的可能重复项
-
这也很愚蠢:仅cut不能执行此任务。您需要其他工具。因此,您不能使用sed或awk,但是可以使用grep和rev?调用内联python或perl脚本怎么办?完全不使用cut怎么办?
-
@MestreLion很多时候,人们阅读问题以找到解决问题的方法。这从错误的前提开始,即cut支持它不支持的功能。但是我认为它很有用,因为它迫使读者考虑更易于遵循的代码。我想要一种快速,简单的使用cut的方法,而无需对awk,grep,sed等使用多种语法。非常优雅,而且我从未考虑过(即使在其他情况下笨拙)。我也喜欢从其他答案中阅读其他方法。
-
@EliranMalka谢谢。感谢您的反馈。我在尝试。
-
这是一个现实生活中的问题:我想在源代码树中找到所有不同的文件扩展名,以更新.gitattributes文件。所以find | cut -d. -f是自然倾向
-
顺便说一句,@ studog,如果在GNU平台上,find . -printf %f
将仅自行发出文件名。
您可以尝试这样的事情:
1
| echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev |
说明
-
maps.google.com的反向为moc.elgoog.spam
-
cut使用点作为定界符并选择第一个字段,即moc
-
最后,我们再次将其反转(感谢提醒@tom)以获得com
-
这个"仅cut"如何?
-
它不仅使用cut,而且不使用sed或awk。
-
在过去的几个小时中,@ tom OP不仅问了更多问题。根据与OP的交互,我们知道awk / sed / etc。不允许在他的家庭作业中使用,但尚未提及rev。所以值得一试
-
@zfus我明白了。之后可能要粘贴另一个rev。
-
@zfus我认为您还需要另一个rev,因为在类似echo www.google.com | rev | cut -d. -f 1 | rev的情况下。
-
双rev非常理想!
-
很棒,简单,完美,也感谢您的解释-没有足够的人来解释管道命令的长链中的每个步骤
-
如果您没有rev,请尝试perl -ple $_ = reverse
使用参数扩展。这比包括cut(或grep)的任何种类的外部命令要高效得多。
1 2
| data=foo,bar,baz,qux
last=${data##*,} |
有关bash中本机字符串操作的介绍,请参见 BashFAQ#100 。
-
@ErwinWessels:因为bash真的很慢。使用bash运行管道,而不是批量处理数据。我的意思是,如果您在shell变量中已经有一行文本,或者您想执行while IFS= read -ra array_var; do :;done <(cmd)处理几行文本,那么这很好。但是对于大文件,rev | cut | rev可能更快! (当然awk会比那快。)
-
@PeterCordes,对于大文件来说,awk肯定会更快,但是要克服恒定因素的启动成本,awk会花费很多。 (还存在一些shell,例如ksh93,其性能接近awk,在此答案中给出的语法仍然有效; bash异常缓慢,但它甚至无法接近唯一可用的选项)。
-
谢谢@PeterCordes;像往常一样,我猜每个工具都有其用例。
-
到目前为止,这是在bash脚本中精简单个变量的最快,最简洁的方法(假设您已经在使用bash脚本)。无需调用任何外部。
-
虽然这看起来确实很整洁,但我还是更喜欢double rev,它不是bash特有的。它学到了我一个新工具!请注意,我一直使用bash,但永远不会提醒那些古怪而野蛮的语法。
-
@Balmipour,...,但是rev特定于您使用的提供此功能的任何操作系统-尚未在所有UNIX系统上标准化。有关命令和实用程序的POSIX部分,请参见列出的章节-此处不存在。而且${var##prefix_pattern}实际上不是bash特定的;它在POSIX sh标准中,请参阅第2.6.2节(链接)的结尾,因此与rev不同,它始终可在任何兼容的shell上使用。
-
@Balmipour,...如果您从事学习新工具的业务,则可以考虑学习具有良好运行时性能特征的工具的好处。 shell之所以以速度慢而闻名,其原因的一半是因为许多人习惯于编写效率低下的脚本,并在内部脚本会用时使用外部命令。 (另一半是我和Peter先前讨论的问题-解释器的性能-但是如果您在紧密的循环中扩展外部工具,那么比较起来,解释器的性能就不那么明显了)。
-
@Charles Duffy感谢您的精确度。我从不需要外壳脚本中的性能,但是就这一点以及它的POSIX标准(我不知道)的事实而言,您显然是正确的。猜猜选择在很大程度上取决于需求,但是我很乐意对此有所了解:)
-
要求切|转速快一点是完全没有根据的。与字符串扩展相比,它非常慢。在我的系统上,字符串扩展的10000次重复花费0.398秒。 rev | cut | rev耗时1分钟6秒
-
@ Bruno9779,因此,它取决于实现细节。如果您为要反转的每个字符串增加一个新的管道,那将非常慢-正如您所注意到的。如果您通过发送10,000个字符串来重用单个管道,则它会比同等的本机bash更快-这大概就是PeterCordes所说的。就是说,单管道重用实际上很少是实际可行的,因此我同意您的观点,通常来说,参数扩展是正确的选择。
-
@ Bruno9779,...提供了一个仅使用一个管道即可非常快速地处理大量行的具体示例:在for ((i=0; i<10000; i++)); do echo"foo,bar,baz,$RANDOM"; done >file; time { rev devnull; }中,time覆盖的部分在我的本地系统上花费了0m0.026s挂钟时间。
-
您可以概括一下以得出最后一个领域的结论吗? rev|cut|rev答案很容易适应以获取任何字段...
-
@GiacomoAlzetta,n=2; IFS=, read -r -a fields; echo"${fields[${#fields[@]}-n]}"-在ideone.com/gMUu1x上查看它的运行情况
仅使用cut是不可能的。这是使用grep的方法:
用逗号分隔其他定界符。
-
若要执行相反的操作,并找到除最后一个字段以外的所有内容,请执行以下操作:grep -o ^.*,
-
这特别有用,因为在我的情况下rev添加了问题多字节unicode字符。
-
我试图在MinGW上执行此操作,但是我的grep版本不支持-o,所以我使用了sed s^.*,,该字符用空字符串替换直到最后一个逗号(包括最后一个逗号)的所有字符。
没有awk吗?
但是使用awk是如此简单:
1
| echo 'maps.google.com' | awk -F. '{print $NF}' |
AWK是一种功能更强大的工具,可以放在口袋里。
-F如果用于字段分隔符
NF是字段数(也代表最后一个的索引)
-
这是通用的,并且每次都完全按预期运行。在这种情况下,使用cut实现OP的最终输出就像使用勺子"切"牛排(双关语:))。 awk是牛排刀。
-
避免不必要地使用echo,这可能会降低使用awk -F. {print $NF} <<< maps.google.com的长文件的脚本速度。
有多种方法。您也可以使用它。
1 2 3
| echo"Your string here"| tr ' ' '
' | tail -n1
> here |
显然,tr命令的空格输入应替换为所需的定界符。
-
谢谢!在busybox sh 1.0.0中起作用的东西:)
-
这感觉像是对我来说最简单的答案,更少的管道和更清晰的含义
-
这不适用于整个文件,这可能是OP的意思。
这是仅使用cut的唯一可能解决方案:
echo"s.t.r.i.n.g." | cut -d'.' -f2-
[repeat_following_part_forever_or_until_out_of_memory:] | cut -d'.' -f2-
使用此解决方案,字段的数量确实可以是未知的,并且会不时变化。但是,由于行长不得超过LINE_MAX个字符或字段(包括换行符),因此,绝对不能将任意数量的字段作为此解决方案的实际条件。
是的,这是一个非常愚蠢的解决方案,但是唯一符合我认为标准的解决方案。
-
真好就拿最后一个。脱离" s.t.r.i.n.g."这可行。
-
当每个人都说某事是不可能的,然后有人听到一个可行的答案时,我会喜欢。即使确实很傻。
-
可以循环循环cut -f2-,直到输出不再更改为止。
如果您的输入字符串不包含正斜杠,则可以使用basename和一个子shell:
1
| $ basename"$(echo 'maps.google.com' | tr '.' '/')" |
这不使用sed或awk,但是也没有使用cut,因此我不确定它是否可以用措词回答问题。
如果处理可能包含正斜杠的输入字符串,这将无法正常工作。解决该问题的方法是将正斜杠替换为您知道不是有效输入字符串的一部分的其他字符。例如,文件名中也不允许使用竖线(|)字符,因此可以使用:
1
| $ basename"$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/' |
以下实现朋友的建议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #!/bin/bash
rcut(){
nu="$( echo $1 | cut -d"$DELIM" -f 2- )"
if ["$nu" !="$1" ]
then
rcut"$nu"
else
echo"$nu"
fi
}
$ export DELIM=.
$ rcut a.b.c.d
d |
-
您需要在echo的参数两边加上双引号,以使其可靠且可靠地工作。见stackoverflow.com/questions/10067266/
为这个老问题添加一个方法只是为了好玩:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info
$ cat tmp.sh # showing off the script to do the job
#!/bin/bash
delim=';'
while read -r line; do
while [["$line" =~"$delim" ]]; do
line=$(cut -d"$delim" -f 2- <<<"$line")
done
echo"$line"
done < input.file
$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info |
除了bash,仅使用cut。
好吧,我想是回声。
-
嗯,为什么不完全删除剪切并仅使用bash ... x] while read -r line; do echo ${line*;}; done 会产生相同的结果。
如果您有一个名为filelist.txt的文件,该文件是诸如以下内容的列表路径:
c:/dir1/dir2/file1.h
c:/dir1/dir2/dir3/file2.h
那么您可以执行以下操作:
rev filelist.txt |切-d" /" -f1 |转速
我意识到,只要确保存在尾随定界符,它就会起作用。因此,在我的情况下,我有逗号和空格分隔符。我在最后添加一个空格;
1 2 3
| $ ans="a, b"
$ ans+=""; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b |
-
并且ans="a, b, c"产生b,它不满足"字段数未知或每行更改"的要求。