如何在 Bash 中检索命令输出的第一个单词?

How can I retrieve the first word of the output of a command in Bash?

我有一个命令,例如:echo"word1 word2"。我想放置一个管道 (|) 并从命令中获取 "word1"。

1
echo"word1 word2" | ....

管道后面应该放什么?


如果您必须处理尾随空格,AWK 是一个不错的选择,因为它会为您处理好它:

1
echo"   word1  word2" | awk '{print $1;}' # Prints"word1"

cut 不会处理这个问题:

1
echo"  word1  word2" | cut -f 1 -d"" # Prints nothing/whitespace

\\'cut\\' 此处不打印任何内容/空格,因为空格之前的第一件事是另一个空格。


没有任何需要使用外部命令。 Bash 本身可以完成这项工作。假设 "word1 word2" 你从某个地方得到并存储在一个变量中,例如,

1
2
3
4
5
6
$ string="word1 word2"
$ set -- $string
$ echo $1
word1
$ echo $2
word2

现在您可以根据需要将 $1$2 等分配给另一个变量。


我认为一种有效的方法是使用 Bash 数组:

1
2
array=( $string ) # Do not use quotes in order to allow word expansion
echo ${array[0]}  # You can retrieve any word. Index runs from 0 to length-1

另外,您可以直接在管道中读取数组:

1
echo"word1 word2" | while read -a array; do echo"${array[0]}" ; done


1
echo"word1 word2 word3" | { read first rest ; echo $first ; }

这样做的好处是不使用外部命令,并且保持 $1、$2 等变量不变。


使用 shell 参数扩展 %% *

这是另一种使用 shell 参数扩展的解决方案。它处理第一个单词后的多个空格。处理第一个单词前面的空格需要一个额外的扩展。

1
2
3
4
5
6
7
string='word1    word2'
echo ${string%% *}
word1

string='word1    word2      '
echo ${string%% *}
word1

解释

%% 表示在 string 的尾部删除 a€?* 的最长可能匹配项(空格后跟任意数量的其他字符)。


如果你确定没有前导空格,你可以使用 Bash 参数替换:

1
2
3
$ string="word1  word2"
$ echo ${string/%\\ */}
word1

注意逃离单个空间。有关替换模式的更多示例,请参见此处。如果您的 Bash > 3.0,您还可以使用正则表达式匹配来处理前导空格 - 请参见此处:

1
2
3
4
$ string="  word1   word2"
$ [[ ${string} =~ \\ *([^\\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1

你可以试试 AWK:

1
echo"word1 word2" | awk '{ print $1 }'

使用 AWK 可以很容易地选择您喜欢的任何单词($1$2 等)。


我想知道几个最佳答案在速度方面是如何衡量的。我测试了以下内容:

1 @mattbh\\'s

1
echo"..." | awk '{print $1;}'

2 @ghostdog74\\'s

1
string="..."; set -- $string; echo $1

3 @boontawee-home\\'s

1
echo"..." | { read -a array ; echo ${array[0]} ; }

和 4 @boontawee-home\\'s

1
echo"..." | { read first _ ; echo $first ; }

我在 macOS 上的 zsh 终端中的 Bash 脚本中使用 Python 的 timeit 测量了它们,使用了一个包含 215 个 5 字母单词的测试字符串。我每次测量 5 次(结果都是 100 次循环,最好的 3 次),然后平均结果:

1
2
3
4
5
6
Method       Time
--------------------------------
1. awk       9.2 ms
2. set       11.6 ms (1.26 *"1")
3. read -a   11.7 ms (1.27 *"1")
4. read      13.6 ms (1.48 *"1")


1
echo"word1 word2" | cut -f 1 -d""

cut 从由字符串 " " (-d"") 分隔的字段列表中剪切第一个字段 (-f 1)。


read 是你的朋友:

  • 如果字符串在变量中:

    1
    2
    3
    4
    string="word1 word2"
    read -r first _ <<<"$string"
    printf '%s\
    '
    "$first"
  • 如果您在管道中工作:第一种情况:您只需要第一行的第一个单词:

    1
    2
    3
    printf '%s\
    '
    "word1 word2""line2" | { read -r first _; printf '%s\
    '
    "$first"; }

    第二种情况:你想要每行的第一个单词:

    1
    2
    3
    printf '%s\
    '
    "word1 word2""worda wordb" | while read -r first _; do printf '%s\
    '
    "$first"; done

如果有前导空格,这些工作:

1
2
3
printf '%s\
'
"   word1 word2" | { read -r first _; printf '%s\
'
"$first"; }

由于 Perl 包含了 AWK 的功能,这也可以用 Perl 来解决:

1
echo" word1 word2" | perl -lane 'print $F[0]'


我正在使用既没有 Perl、AWK 或 Python 的嵌入式设备,而是使用 sed 来完成。它支持在第一个单词之前有多个空格(cutbash 解决方案无法处理)。

1
2
VARIABLE="  first_word_with_spaces_before_and_after  another_word "
echo $VARIABLE | sed 's/ *\\([^ ]*\\).*/\\1/'

这在 grepping ps 以获取进程 ID 时非常有用,因为此处仅使用 Bash 的其他解决方案无法删除 ps 用于对齐的第一个空格。