쉘 명령 출력의 문자 수


12

단일 단계 에서 명령 출력의 문자 수를 계산 해야하는 스크립트를 작성 중 입니다.

예를 들어, 해당 명령 의 출력이 10 자이므로 명령을 사용하여 readlink -f /etc/fstab리턴해야합니다 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 쉘 (sh)과의 호환성이 바람직합니다.


1
출력 readlink -f /etc/fstab11 자입니다. 개행을 잊지 마십시오. 그렇지 않으면 /etc/fstabluser@cern:~$ 쉘에서 실행할 때 표시 됩니다.
Phil Frost

@ PhilFrost 당신은 재미있는 프롬프트가있는 것 같습니다, 당신은 CERN에서 일하십니까?
Dmitry Grigoryev

답변:


9

GNU의 EXPR :

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

(가) +GNU의 특별한 기능이 expr있는지 다음 인수가 될 일 경우에도 문자열로 처리하기 위해 expr같은 운영자 match, length, +...

위의 모든 줄 바꿈 출력을 제거합니다. 해결하려면 다음을 수행하십시오.

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

마지막 줄 바꿈 과 추가 한 문자 때문에 결과는 2 로 뺍니다 .readlink.

유니 코드 문자열을 사용하면 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

명령 대체 전의 공백은 명령이 string start로 충돌하는 것을 방지 -하므로 3을 빼야합니다.


감사! 세 번째 예제는을 사용하지 않아도 작동하므로 LC_ALL=C.UTF-8문자열 인코딩을 미리 알 수없는 경우 크게 단순화합니다.
user339676

2
expr length $(echo "*")— 아뇨. 적어도 큰 따옴표를 사용하십시오 : expr length "$(…)". 그러나 이것은 명령에서 후행 줄 바꿈을 없애고 명령 대체의 피할 수없는 기능입니다. (당신은 그 문제를 해결할 수는 있지만 그 대답은 훨씬 더 복잡해집니다.)
Gilles 'SO- 악의를 그만두십시오

6

쉘 내장 ( Gnouc는 그래도 ) 으로이 작업을 수행하는 방법을 모르지만 표준 도구가 도움이 될 수 있습니다.

  1. wc -m문자 수를 사용할 수 있습니다 . 불행히도 최종 줄 바꿈을 계산하므로 먼저 제거해야합니다.

    readlink -f /etc/fstab | tr -d '\n' | wc -m
  2. 물론 사용할 수 있습니다 awk

    readlink -f /etc/fstab | awk '{print length($0)}'
  3. 또는 펄

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

expr내장되어 있다는 것을 의미 합니까? 어느 껍질에서?
mikeserv

5

나는 보통 다음과 같이합니다 :

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

명령을 수행하려면 다음과 같이 조정하십시오.

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

이 접근법은 단일 단계의 라이너로 결합한다는 점을 제외하고는 2 단계에서 수행 한 작업과 유사합니다.


2
-m대신 사용해야합니다 -c. 유니 코드 문자를 사용하면 접근 방식이 손상됩니다.
cuonglm

1
왜 간단하지 readlink -f /etc/fstab | wc -m않습니까?
Phil Frost

1
왜이 신뢰할 수없는 방법을 대신 사용 ${#variable}합니까? 적어도 큰 따옴표를 사용 echo -n "$variable"하지만 예를 들어 값이 variableis 인 경우 여전히 실패합니다 -e. 명령 대체와 함께 사용하면 마지막 줄 바꿈이 제거됩니다.
Gilles 'SO- 악의를 멈춰라

@ philfrost b / c 내가 보여준 것은 op가 이미 생각한 것에서 나온 것입니다. 또한 이전에 vars에서 설정했을 수 있고 모든 길이의 결과를 원하는 모든 cmd에서 작동합니다. 또한 terdon은 그 예를 이미 가지고 있습니다.
slm

1

외부 유틸리티를 호출 할 수 있지만 (다른 답변 참조) 스크립트 속도가 느려져 배관을 제대로 맞추기가 어렵습니다.

Zsh

zsh에서는 ${#$(readlink -f /etc/fstab)}명령 대체 길이를 얻기 위해 쓸 수 있습니다 . 이것은 명령 출력의 길이가 아니며 마지막 줄 바꿈이없는 출력의 길이입니다.

출력의 정확한 길이를 원하면 끝에 줄 바꿈 문자가 아닌 추가 문자를 출력하고 빼십시오.

$((${#$(readlink -f /etc/fstab; echo .)} - 1))

명령 출력의 페이로드가 원하는 경우 출력이 표준 경로에 개행 문자를 더하기 때문에 여기에서 두 개 를 빼야 readlink -f합니다.

$((${#$(readlink -f /etc/fstab; echo .)} - 2))

이것은 ${#$(readlink -f /etc/fstab)}정식 경로 자체가 개행으로 끝나는 드물지만 가능한 경우 와 다릅니다 .

이 특정 예제의 경우, zsh에는 readlink -fhistory 수정자를 통해 와 동일한 내장 구성이 있으므로 외부 유틸리티가 전혀 필요하지 않습니다 A.

echo /etc/fstab(:A)

길이를 얻으려면 매개 변수 확장에서 히스토리 수정자를 사용하십시오.

${#${:-/etc/fstab}:A}

당신이 변수에 파일 이름이있는 경우 filename, 그 것이다 ${#filename:A}.

Bourne / POSIX 스타일 쉘

순수한 Bourne / POSIX 쉘 (Bourne, ash, mksh, ksh93, bash, yash ...) 중 어느 것도 내가 아는 비슷한 확장 기능이 없습니다. 명령 대체 출력에 매개 변수 대체를 적용하거나 매개 변수 대체를 중첩해야하는 경우 연속 단계를 사용하십시오.

원하는 경우 처리를 함수에 넣을 수 있습니다.

command_output_length_sans_trailing_newlines () {
  set -- "$("$@")"
  echo "${#1}"
}

또는

command_output_length () {
  set -- "$("$@"; echo .)"
  echo "$((${#1} - 1))"
}

그러나 일반적으로 혜택은 없습니다. ksh93을 제외하면 여분의 포크가 함수의 출력을 사용할 수 있으므로 스크립트 속도가 느려지고 가독성이 거의 없습니다.

다시 한번, 출력은 readlink -f표준 경로와 개행입니다. 표준 경로의 길이를 원하면에서 1 대신 2를 빼십시오 command_output_length. command_output_length_sans_trailing_newlines표준 경로 자체가 개행으로 끝나지 않을 때만 사용 하면 올바른 결과를 얻을 수 있습니다.

바이트 대 문자

${#…}멀티 바이트 로케일에서 차이를 만드는 바이트 단위가 아닌 문자 단위의 길이 여야합니다. 합리적으로 최신 버전의 ksh93, bash 및 zsh LC_CTYPE${#…}구성이 확장 될 때의 값에 따라 문자 길이를 문자로 계산합니다 . 다른 많은 공통 쉘은 실제로 멀티 바이트 로케일을 지원하지 않습니다. 대시 0.5.7, mksh 46 및 posh 0.12.3 ${#…}에서 길이는 바이트 단위로 리턴됩니다. 신뢰할 수있는 방식으로 문자 길이를 원하면 wc유틸리티를 사용하십시오 .

$(readlink -f /etc/fstab | wc -m)

$LC_CTYPE유효한 로캘 을 지정하면이 오류가 발생하거나 (멀티 바이트 로캘을 지원하지 않는 고대 또는 제한된 플랫폼에서) 올바른 길이의 문자를 반환 할 것이라고 확신 할 수 있습니다. 유니 코드의 경우 "문자 길이"는 코드 포인트 수를 의미합니다. 글리프 수는 문자 결합과 같은 복잡한 문제로 인해 또 다른 이야기입니다.

바이트 단위의 길이를 원하면 LC_CTYPE=C임시로 설정 하거나 wc -c대신 사용하십시오 wc -m.

바이트 또는 문자를 계산 wc하면 명령의 마지막 줄 바꿈 이 포함됩니다. 표준 경로의 길이를 바이트 단위로 원하면

$(($(readlink -f /etc/fstab | wc -c) - 1))

문자로 나타내려면 2를 빼십시오.


@cuonglm 아니오, 1을 빼야 echo .합니다. 두 문자를 더하지만 두 번째 문자는 후행 줄 바꿈이며 명령 대체로 제거됩니다.
Gilles 'SO- 악의를 멈춰라'

줄 바꿈은 readlink출력에서 시작하여 .by echo입니다. 우리는 echo .두 문자 를 추가하지만 후행 줄 바꿈이 제거 되었다는 데 동의합니다 . 시도 printf .하거나 내 대답 unix.stackexchange.com/a/160499/38906을 참조하십시오 .
cuonglm

@cuonglm이 질문은 명령 출력의 문자 수를 물었습니다. 의 결과 readlink는 링크 대상에 줄 바꿈을 더한 것입니다.
Gilles 'SO- 악의를 멈춰라'

0

이것은 작동 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

그것은 물론 모든 쉘 내장- readlink물론 포함 하지는 않지만 현재 쉘에서 그것을 평가하는 것은 len을 얻기 전에 할당을 수행해야 함을 의미 %.s하므로 printf형식 문자열 의 첫 번째 인수를 무시하고 다시 추가합니다. printf인수리스트 의 꼬리에있는 리터럴 값

eval:

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

산출

10:/etc/fstab

같은 것에 가까워 질 수 있지만 첫 번째 명령의 변수 출력 대신 stdout에 표시됩니다.

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

... 쓴다 ...

10:/etc/fstab

... 현재 쉘의 변수에 값을 할당하지 않고 파일 설명자 1에.


1
그게 OP가 피하고 싶었던 것이 아닙니까? "출력을 변수에 먼저 저장하여이 작업을 수행 할 수 있다는 것을 알고 있습니다. variable=$(readlink -f /etc/fstab); echo ${#variable};그러나 추가 단계를 제거하고 싶습니다."
terdon

@ terdon, 아마 오해했을 것입니다. 그러나 세미콜론이 문제가 아니라 변수라는 것이 인상이었습니다. 이것이 쉘 내장 만 사용하여 하나의 간단한 명령으로 len 및 출력을 얻는 이유입니다. 예를 들어 쉘은 readlink 실행 하지 않고 exec 실행 expr합니다. 아마 단지 어떻게 든이있을 수 있습니다 왜 어려움을 인식하지 못했습니다 인정 len의 폐색 값을 얻는 경우에 중요한,하지만 난 그게 중요하는 경우가있을 수 있습니다 생각한다.
mikeserv

1
eval방법은, 그런데, 아마 여기에 깨끗한입니다 - 출력 및 단일 실행에서 같은 VAR 이름으로 렌 할당 - 매우 가까운 일에 l=length(l):out(l). 이렇게는 expr length $(command) 하지 그런데, 렌 대신 값을 폐색.
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.