shell 命令输出中的字符数

shell 命令输出中的字符数

我正在编写一个脚本,需要计算命令输出中的字符数一步

例如,使用该命令readlink -f /etc/fstab应该返回,10因为该命令的输出有 10 个字符长。

使用以下代码可以通过存储变量实现这一点:

variable="somestring";
echo ${#variable};
# 10

不幸的是,对命令生成的字符串使用相同的公式不起作用:

${#(readlink -f /etc/fstab)};
# bash: ${#(readlink -f /etc/fstab)}: bad substitution

我知道可以通过首先将输出保存到变量来做到这一点:

variable=$(readlink -f /etc/fstab);
echo ${#variable};

但我想删除额外的步骤。

这可能吗?最好仅使用内置或标准实用程序与 Almquist shell (sh) 兼容。

答案1

GNU 表达式:

$ expr length + "$(readlink -f /etc/fstab)"
10

GNU+有一个特殊功能,expr可以确保下一个参数被视为字符串,即使它恰好是像, , ...expr这样的运算符。matchlength+

上面的代码将去除输出中任何尾随的换行符。要解决这个问题:

$ expr length + "$(readlink -f /etc/fstab; printf .)" - 2
10

结果被减去2因为最后的换行符和我们添加的readlink字符。.

对于 Unicode 字符串,expr似乎不起作用,因为它返回字符串的长度(以字节为单位)而不是字符数(请参阅654线

$ LC_ALL=C.UTF-8 expr length ăaa
4

所以,你可以使用:

$ printf "ăaa" | LC_ALL=C.UTF-8 wc -m
3

正面:

$ expr " $(readlink -f /etc/fstab; printf .)" : ".*" - 3
10

命令替换之前的空格可以防止命令以 开头的字符串崩溃-,所以我们需要减去 3。

答案2

不知道如何使用 shell 内置函数来做到这一点(虽然 Gnouc 是)但标准工具可以帮助:

  1. 您可以使用wc -m它来计算字符数。不幸的是,它也计算最后的换行符,所以你必须首先删除它:

    readlink -f /etc/fstab | tr -d '\n' | wc -m
    
  2. 你当然可以使用awk

    readlink -f /etc/fstab | awk '{print length($0)}'
    
  3. 或者 Perl

    readlink -f /etc/fstab | perl -lne 'print length'
    

答案3

我通常这样做:

$ echo -n "$variable" | wc -m
10

为了执行命令,我会像这样调整它:

$ echo -n "$(readlink -f /etc/fstab)" | wc -m
10

这种方法与您在 2 个步骤中所做的类似,只是我们将它们组合成一个衬里。

答案4

这可以工作,dash但它确实要求目标变量绝对为空或未设置。这就是为什么这实际上是命令 - 我$l在第一个命令中明确清空:

l=;printf '%.slen is %d and result is %s\n' \
    "${l:=$(readlink -f /etc/fstab)}" "${#l}" "$l"

输出

len is 10 and result is /etc/fstab

这就是所有 shell 内置函数 - 当然不包括readlink- 但在当前 shell 中对其进行评估意味着您必须在获取 len 之前进行赋值,这就是为什么我%.s忽略格式字符串中的第一个参数printf并再次添加它printfarg 列表尾部的文字值。

eval

l=$(readlink -f /etc/fstab) eval 'l=${#l}:$l'
printf %s\\n "$l"

输出

10:/etc/fstab

您可以接近同样的事情,但是您可以在标准输出上获取它,而不是第一个命令中变量的输出:

PS4='${#0}:$0' dash -cx '2>&1' "$(readlink -f /etc/fstab)"

...其中写...

10:/etc/fstab

...到文件描述符 1,而不为当前 shell 中的任何变量分配任何值。

相关内容