按空格字符拆分字符串(基本名称的奇怪问题)

按空格字符拆分字符串(基本名称的奇怪问题)

我需要将输出分开ps,它是间隔开的。

#!/bin/bash

A=$(ps -r -e -o pcpu=,comm= | head -1)
B=${A[0]}
C=${A[1]}
printf '%3s  %s\n' $B $(basename $C)

输出应该是:

 42 bar

相反,我得到:

usage: basename ...

为什么这不起作用,最重要的是,我该如何让它起作用?

答案1

要按空格字符拆分字符串,可以使用 split+glob 运算符。这是在命令替换或参数扩展时调用的,但显然在存储到标量最多只能存储一个值的变量。

 var=$(...)

是标量变量赋值。因此会将整个输出分配给$var,与 相同${var[0]}。对于数组变量赋值,它是:

 var=($(...))

现在,在调用它之前,像往常一样,您需要对其进行调整:

 set -o noglob # disable the glob part which you don't want here
 IFS=' ' # split on space only
 var=($(some command)) # invoke it

请注意,它会分裂序列空格以及前导空格和尾随空格将被忽略。

然后,您不想在$BNor的扩展上调用它$C,因此$(basename...)您需要引用它们:

 printf '%3s  %s\n' "$B" "$(basename -- "$C")"

另外不要忘记--标记选项的结尾。

因为您忘记了周围的引号$C$C在您的情况下为空,因为${A[1]}从未分配任何值),basename所以没有传递任何参数,而不是传递该空参数并抱怨它。

答案2

其他人已经注意到代码中的错误是什么,并正确建议对于初始占位符数据,数组将是更好的数据结构选择,以及如何确保正确分割字符串等。

现在我们知道您正在解析的实际命令是什么,我们可以更有创意地提出改进建议。

以下脚本将获取命令输出的每一行ps并将其读取为两个空格分隔的位。循环体以不同的方式输出读取的位:

#!/bin/bash

ps -r -e -o pcpu=,comm= |
while IFS=' ' read -r pcpu comm; do
    printf 'pcpu=%s,\tcomm=%s,\tbasename of comm=%s\n' \
        "$pcpu" "$comm" "${comm##*/}"
done

在这里,comm将保存输出中第一个空格序列之后的所有内容ps(第一列之前的初始空格将被修剪掉)。

如果您愿意,您显然可以将其head -n 1作为初始管道的一部分插入。

请注意,在某些 shell(包括 )中bash,循环在子 shell 中运行,因此在管道完成后,其中创建的任何变量将不可用。对此有两种解决方案bash

  1. 使用, 或lastpipe在脚本中启用shell 选项shopt -s lastpipe
  2. 通过过程替换将数据读入循环:

    while IFS=' ' read ...
       # ...
    done < <( ps ... )
    

运行示例:

$ bash script.sh
pcpu=0.0,       comm=tmux,      basename of comm=tmux
pcpu=0.0,       comm=sh,        basename of comm=sh
pcpu=0.0,       comm=sh,        basename of comm=sh
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=ps,        basename of comm=ps
pcpu=0.0,       comm=sh,        basename of comm=sh

答案3

使用 () 定义数组。如果使用双引号,那么它将作为整个字符串。

阅读更多关于大批

$ cat test.sh
#!/bin/bash

A=(42 /foo/bar)
B=${A[0]}
C=${A[1]}
printf '%3s  %s\n' $B $(basename $C)

$ bash test.sh
 42  bar

答案4

另一种使用 awk 的方法:

 echo "42 /foo/bar" | awk '{n=split($2,b,"/"); print $1,b[n]}'

相关内容