ChatGPT解决这个技术问题 Extra ChatGPT

如何使用'cut'找到最后一个字段

没有使用sedawkcut,当字段数未知或每行都在变化时,如何获取最后一个字段?

您喜欢 cut 命令吗 :)?为什么没有其他 Linux 命令?
没有 sedawkperl -pe 's/^.+\s+([^\s]+)$/$1/'
@MestreLion 很多时候,人们阅读问题是为了找到解决问题变体的方法。这首先是一个错误的前提,即 cut 支持它不支持的东西。但我认为它很有用,因为它迫使读者考虑更容易理解的代码。我想要一种快速、简单的方式来使用 cut,而无需对 awkgrepsed 等使用多种语法。rev 的事情成功了;非常优雅,而且我从未考虑过(即使在其他情况下很笨重)。我也喜欢从其他答案中阅读其他方法。
来了一个现实生活中的问题:我想在源树中找到所有不同的文件扩展名,以更新 .gitattributes 文件。所以 find | cut -d. -f<last> 是自然倾向

S
Sled

你可以尝试这样的事情:

echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev

解释

rev 将“maps.google.com”反转为 moc.elgoog.spam

cut 使用点(即'.')作为分隔符,并选择第一个字段,即 moc

最后,我们再次反转它以获得 com


它不仅使用 cut,而且没有 sedawk。那么 OP 怎么想?
@tom OP 在过去几个小时内提出了更多问题。根据我们与 OP 的交互,我们知道 awk/sed/etc.不允许在他的作业中使用,但没有提到 rev。所以值得一试
@zfus 我明白了。之后可能要粘贴另一个 rev
rev伟大的理想!
太棒了,简单,完美,也感谢您的解释 - 没有足够的人解释长链管道命令中的每个步骤
C
Charles Duffy

使用参数扩展。这比任何类型的外部命令(包括 cut(或 grep))都高效得多。

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 对于大文件当然会更快,但是需要相当多的输入才能克服恒定因素的启动成本。 (还有一些 shell——比如 ksh93——性能更接近于 awk,这个答案中给出的语法仍然有效;bash 异常缓慢,但它甚至不接近唯一可用的选项)。
谢谢@PeterCordes;像往常一样,我猜每个工具都有其用例。
这是迄今为止在 bash 脚本中缩减单个变量的最快、最简洁的方法(假设您已经在使用 bash 脚本)。无需调用任何外部的东西。
@Balmipour,...但是,rev 特定于您使用的任何提供它的操作系统——它不是在所有 UNIX 系统中标准化的。请参阅 chapter listing for the POSIX section on commands and utilities - 它不存在。而 ${var##prefix_pattern} 实际上 不是 特定于 bash 的;它在 POSIX sh standard 中,请参阅第 2.6.2 节的末尾(链接),因此与 rev 不同,它始终在任何兼容的 shell 上可用。
t
tom

仅使用 cut 是不可能的。这是使用 grep 的一种方式:

grep -o '[^,]*$'

替换其他分隔符的逗号。

解释:

-o (--only-matching) 仅输出与模式匹配的输入部分(如果包含匹配项,则默认打印整行)。

[^,] 是匹配除逗号以外的任何字符的字符类。

* 匹配前面的模式零次或多次,因此 [^,]* 匹配零个或多个非逗号字符。

$ 匹配字符串的结尾。

综上所述,该模式匹配字符串末尾的零个或多个非逗号字符。

当有多个可能的匹配时,grep 会选择最早开始的那个。所以整个最后一个字段将被匹配。

完整示例:

如果我们有一个名为 data.csv 的文件,其中包含

one,two,three
foo,bar

然后 grep -o '[^,]*$' < data.csv 将输出

three
bar

反之,查找除最后一个字段之外的所有内容:grep -o '^.*,'
这特别有用,因为 rev 在我的例子中添加了一个问题多字节 unicode 字符。
我试图在 MinGW 上执行此操作,但我的 grep 版本不支持 -o,因此我使用 sed 's/^.*,//' 将所有字符替换为空字符串(包括最后一个逗号)。
A
Amir Mehler

没有 awk ?...但是使用 awk 就这么简单:

echo 'maps.google.com' | awk -F. '{print $NF}'

AWK 是一种更强大的工具,可以放在你的口袋里。 -F 如果对于字段分隔符 NF 是字段数(也代表最后一个的索引)


这是通用的,并且每次都按预期工作。在这种情况下,使用 cut 来实现 OP 的最终输出就像使用勺子“切”牛排(双关语:))。 awk 是牛排刀。
避免不必要地使用 echo,这可能会减慢使用 awk -F. '{print $NF}' <<< 'maps.google.com' 的长文件的脚本。
r
rjni

有多种方法。你也可以用这个。

echo "Your string here"| tr ' ' '\n' | tail -n1
> here

显然,tr 命令的空格输入应该替换为您需要的分隔符。


这对我来说感觉是最简单的答案,更少的管道和更清晰的含义
这不适用于整个文件,这可能是 OP 的意思。
A
A friend

这是只使用 cut 的唯一解决方案:

回声“字符串” |切-d'。 -f2- [repeat_following_part_forever_or_until_out_of_memory:] |切-d'。 -f2-

使用这个解决方案,字段的数量确实是未知的,并且会不时变化。但是,由于行长度不得超过 LINE_MAX 个字符或字段,包括换行符,因此任意数量的字段永远不会成为此解决方案的真实条件。

是的,一个非常愚蠢的解决方案,但唯一符合我认为的标准的解决方案。


好的。只需取最后一个“。”关闭“字符串”,这有效。
我喜欢每个人都说某事是不可能的,然后有人插话给出一个可行的答案。哪怕确实很傻。
可以在循环中迭代 cut -f2-,直到输出不再改变。
我认为您必须逐行阅读文件并 然后 迭代 cut -f2- 直到它不再更改。否则你必须缓冲整个文件。
j
jstine

如果您的输入字符串不包含正斜杠,那么您可以使用 basename 和一个子shell:

$ basename "$(echo 'maps.google.com' | tr '.' '/')"

这不使用 sedawk,但它也不使用 cut,所以我不太确定它是否有资格作为其措辞的问题的答案。

如果处理可以包含正斜杠的输入字符串,这将无法正常工作。这种情况的解决方法是将正斜杠替换为您知道不是有效输入字符串的一部分的其他字符。例如,管道 (|) 字符也不允许出现在文件名中,因此可以这样做:

$ basename "$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/'

当然当然文件名中允许使用管道字符。试试 touch \|
如果您删除关于文件名中不允许 | 的虚假声明,我将从反对票改为赞成票。但是几乎所有的 tr 都支持 \0 或其他表达 nul 字节的方式,而且文件名中绝对不允许这样做,因此您可以将其用作占位符。此外,tr ab bc 只是毫无问题地交换所有 ab,因此您可以完全避免必须找到一个不允许的字符。只需通过 tr './' './' 一次以在 basename 之前交换,然后再次在之后交换回来。
刚刚意识到我有一个错字:“只需通过 tr '/.' './' 管道一次在基本名称之前交换,然后再在之后交换”。
u
user2166700

以下实现A friend's suggestion

#!/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/…
m
moni905

使用 perl 的替代方法是:

perl -pe 's/(.*) (.*)$/$2/' file

无论 file 的分隔符是什么,您都可以在其中更改 \t


a
aperson1961

如果您有一个名为 filelist.txt 的文件,它是一个列表路径,如下所示:c:/dir1/dir2/file1.hc:/dir1/dir2/dir3/file2.h

那么你可以这样做: rev filelist.txt |剪切 -d"/" -f1 |转


K
Kaffe Myers

为这个老问题添加一种方法只是为了好玩:

$ 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。嗯,还有回声,我猜。


嗯,为什么不完全删除 cut 而只使用 bash... x] while read -r line; do echo ${line/*;}; done <input.file 会产生相同的结果。
j
jww

我意识到如果我们只是确保存在尾随分隔符,它就可以工作。所以就我而言,我有逗号和空格分隔符。我在末尾添加了一个空格;

$ ans="a, b"
$ ans+=" "; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b

ans="a, b, c"产生b,不符合“字段数未知或逐行变化”的要求。