我正在編寫一個腳本,需要計算命令輸出中的字元數一步。
例如,使用該命令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 中的任何變數分配任何值。