我正在编写一个脚本,需要计算命令输出中的字符数一步。
例如,使用该命令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
这样的运算符。match
length
+
上面的代码将去除输出中任何尾随的换行符。要解决这个问题:
$ 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 是)但标准工具可以帮助:
您可以使用
wc -m
它来计算字符数。不幸的是,它也计算最后的换行符,所以你必须首先删除它:readlink -f /etc/fstab | tr -d '\n' | wc -m
你当然可以使用
awk
readlink -f /etc/fstab | awk '{print length($0)}'
或者 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
并再次添加它printf
arg 列表尾部的文字值。
和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 中的任何变量分配任何值。