bash에서 변수의 줄 수를 어떻게 계산합니까?


82

문자열이 저장된 변수가 있고 그 안에 줄이 있는지 확인해야합니다.

var=`ls "$sdir" | grep "$input"`

의사 코드 :

while [ ! $var's number of lines -eq 1 ]
  do something

그것이 그것을 확인하는 방법에 대한 나의 생각입니다. echo $var | wc -l작동하지 않습니다 - 항상 말한다 1, 그것은에도 불구하고 3.

echo -e 잘 작동하지 않습니다.

답변:


108

따옴표가 중요합니다.

echo "$var" | wc -l

3
이는 명령을 둘러싼 따옴표가 아니라 명령 대체를 둘러싼 따옴표입니다. 따옴표 쌍은 방해하지 않습니다.
Ignacio Vazquez-Abrams 2011 년

38
이것은 미묘합니다. 빈 문자열의 echo는 개행을 인쇄하므로 빈 문자열은 1을 반환합니다.
Andrew Nguyen

3
즉 : if [ -z "$var" ]; then printf '%s\n' '0'; else printf '%s\n' "${var%$'\n'}" | wc -l; fi. var=(줄 없음) var='foo'var=$'foo\n'(* nix 의미에서 두 줄 모두 ) 시도하십시오 .
l0b0

2
그리고 또 다른 방법은 변수에 그것을 얻을 수 있습니다 : LINE_COUNT=$(wc -l <<< "${var}")
lucifurious

4
@Tim echo -n은 단순히 카운트를 1 씩 줄입니다. @lucifurious echo $(wc -l <<< "${NONEXISTENTVAR}")여전히 1 제공하지 0
줄리안

73

여기에 게시 된 허용 된 답변 및 기타 답변은 빈 변수 (정의되지 않거나 빈 문자열)의 경우 작동하지 않습니다.

이것은 작동합니다 :

echo -n "$VARIABLE" | grep -c '^'

예를 들면 :

ZERO=
ONE="just one line"
TWO="first
> second"

echo -n "$ZERO" | grep -c '^'
0
echo -n "$ONE" | grep -c '^'
1
echo -n "$TWO" | grep -c '^'
2

@PolyTekPatrick에서 버그처럼 들리는 것을 재현 할 수 없습니다. 즉 WHITESPACE_ONE=' ', 쉘 (bash)에서 여전히 작동하여 한 줄을 올바르게보고합니다.
Julian

네가 옳아. 나는 그것을 시험해 보겠다고 맹세했지만 큰 따옴표를 놓쳤거나 무언가를 잘못 입력했을 것입니다. 하나 이상의 공백 문자는 예상대로 실제로 한 줄로 계산됩니다. 사람들의 혼동을 피하기 위해 이전 댓글을 삭제했습니다.
PolyTekPatrick

4
완전한! 이것은 받아 들여진 대답이어야합니다. 지금까지 모든 경우에 대한 질문에 적절하게 대답하는 유일한 솔루션입니다. 그것을 증명하기 위해 테스트 케이스를 보여 주셔서 감사합니다.
PolyTekPatrick

4
예! 저도 이것이 받아 들여진 대답이어야한다고 생각합니다.
Martin Joiner

1
이것은 정확하지만 사용하여 단순화 할 수있다 printf보다는 echo -n. 테스트 결과를 포함하기 위해 이것을 대체 답변으로 추가했습니다. 아래를 참조하십시오.
Stilez

23

bash에서 여기 문자열 을 사용하는 또 다른 방법 :

wc -l <<< "$var"

이 주석 에서 언급했듯이 여기에 문자열 이이 경우 줄 바꿈 문자를 추가 $var하기 때문에 비어 있으면 0 줄 대신 1 줄이됩니다 ( 설명 ).


1
: 나는 정답을 얻기 위해이 작업을 수행했다 wc -l <<<"$(echo "$var")"(예, 모든 단일 기호가 필요했다)
니콜라이 S

@NicolaiS 그럼 당신은 뭔가 잘못했습니다. 내용은 무엇입니까 $var?
speakr

1
@NicolaiS 맞습니다. 한 줄이var 포함되어 있기 때문에 : 당신은 아무것도 해석하지 않았습니다 . 여러 줄을 입력 하면 . \nvarvar="foo<ENTER>bar<ENTER>baz"<ENTER>
speakr

2
xxd <<< ''이 hexdump를 만듭니다 00000000: 0a. 따라서 <<<(Here Strings) 모든 내용에 개행 문자를 추가하십시오.
MiniMax 2017-06-12

1
@MiniMax 감사합니다. 귀하의 의견을 제 답변에 추가했습니다. 여기 에서이 동작에 대한 설명을 찾을 수 있습니다 .
연설자

9

"wc -l"을 "wc -w"로 대체하여 줄 대신 단어 수를 계산할 수 있습니다. 이것은 새로운 줄을 계산하지 않으며 계속하기 전에 원래 결과가 비어 있는지 테스트하는 데 사용할 수 있습니다.


wc -l솔루션은 입력 변수가 비어 있어도 1을 출력하므로 wc -w사용하는 것이 더 좋습니다.
카디르

9

아무도 매개 변수 확장에 대해 언급하지 않았으므로 여기에 순수 bash를 사용하는 몇 가지 방법이 있습니다.

방법 1

줄 바꿈이 아닌 문자를 제거한 다음 문자열 길이 + 1을 가져옵니다. 따옴표는 중요 합니다.

 var="${var//[!$'\n']/}"
 echo $((${#var} + 1))

방법 2

배열로 변환 한 다음 배열 길이를 가져옵니다. 이 작업을 수행하려면 따옴표를 사용하지 마십시오 .

 set -f # disable glob (wildcard) expansion
 IFS=$'\n' # let's make sure we split on newline chars
 var=(${var})
 echo ${#var[@]}

2
방법 2는 더 깨끗하고 아마도 더 빠를 것입니다. 그러나 IFS. 따라서 IFS=$'\n'변수를 배열로 확장 할 때 새 줄에서 분할되도록 설정 하십시오.IFS=$'\n'; var=(${var})
notre

최소한의 오버 헤드 때문에 방법 2를 좋아합니다. 외부 명령이없고 내장 명령도 보이지 않습니다. 또한 꽤 읽기 쉽습니다.
DKroot

1
ShellCheck 그래도 불평 : github.com/koalaman/shellcheck/wiki/SC2206 . 뭔가에 있습니다. set -f원치 않는 glob 확장을 방지하기 위해 필요합니다. 에서 방법 2를 사용해보십시오 var=$'*\n.*'.
DKroot

순수 내부 bash에 대한 자동 보너스 (외부 제외)wc -l
nhed

7

더 간단한 버전의 @Julian의 대답은 후행이 있거나없는 모든 문자열에 대해 작동합니다.

printf "%s" "$a" | grep -c "^"

  • 0 반환 : 설정되지 않은 변수, 빈 문자열, 맨 줄 바꿈이 포함 된 문자열
  • 1을 반환합니다. 후행 개행이 있거나없는 비어 있지 않은 모든 줄
  • 기타

산출:

# a=
# printf "%s" "$a" | grep -c "^"
0

# a=""
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "\n")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf " \n")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf " ")"
# printf "%s" "$a" | grep -c "^"
1

# a="aaa"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n%s" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

# a="$(printf "%s\n%s\n" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

+1. echo(예 :)에 플래그를 전달하는 echo -n것은 표준이 아니며 구현에 따라 다른 결과를 제공 할 수 있습니다. 반면은 printf우리가 기본적으로 원하는 것을. 보너스로을 사용 printf하면 쉘이 내장되어 있으므로 프로세스 를 절약 할 수 있습니다. (제안 : printf "$a" | wc -l더 간결하고 불필요한 사용을 피합니다. grep)
joshtch

@joshtch 음 .. 아니. 이 토론에서 다른 사람들이 말했듯이 빈 줄 의 경우 결과는 0 이되도록 printf .... | wc -l하나의 추가 줄 (개행) 만 제거 합니다. 옳은. 그러나 우리는이 개 라인을 통과하는 경우에 전달 된 같은 변수는 곳, 결과는 1이 될 것입니다 제대로 직접 사용, 게다가 2를 반환합니다 그것이 문자열이 실수로 같은 문자가 포함 된 경우 자동 오류가 발생할 수 있기 때문에 매우 위험합니다 , 그래서 및 on ... 문자열이 대시로 시작하는 경우에도 동일합니다. 대신,이 매개 변수에 자동으로 이스케이프printf ... | grep "^"printf "$a"%s%dprintf
누가 복음 Savefrogs

3

grep이 결과를 반환하지 않으면 가장 많이 투표 한 답변이 실패합니다.

Homer Simpson
Marge Simpson
Bart Simpson
Lisa Simpson
Ned Flanders
Rod Flanders
Todd Flanders
Moe Szyslak

이다 잘못 그것을 할 방법 :

wiggums=$(grep -iF "Wiggum" characters.txt);
num_wiggums=$(echo "$wiggums" | wc -l);
echo "There are ${num_wiggums} here!";

목록에 1 Wiggum 이 없다고 말할 것 입니다.

대신 변수가 비어 있는지 확인하기 위해 한 번의 추가 검사를 수행해야합니다 ( -z, "is 0"에서와 같이). grep이 아무것도 반환하지 않으면 변수는 비어 있습니다.

matches=$(grep -iF "VanHouten" characters.txt);

if [ -z "$matches" ]; then
    num_matches=0;
else
    num_matches=$(echo "$matches" | wc -l);
fi

echo "There are ${num_matches} VanHoutens on the list";

2

변수의 줄 수를 계산하는 또 다른 방법-성공적으로 채워 졌는지 또는 비어 있지 않은지 확인했다고 가정하면 $를 확인하십시오. var subshell 결과 영향 후 -:

readarray -t tab <<<"${var}"
echo ${#tab[@]}

readarray | mapfile 은 입력 파일 또는 이 경우 문자열 을 줄 바꿈 기반 배열 로 변환하는 bash 내부 명령입니다 .

-t 플래그는 배열의 셀 끝에 줄 바꿈을 저장하지 못하게하여 나중에 저장된 값을 사용할 때 유용합니다.

이 방법의 장점은 다음과 같습니다.

  • 외부 명령 없음 (wc, grep, ...)
  • 서브 쉘 없음 (파이프)
  • IFS 문제 없음 (수정 후 복원, 내부 명령에서 명령 제한 범위로 사용하기 까다 로움, ...)

1

"wc -l"명령에서 파일 이름을 피하려면 :

lines=$(< "$filename" wc -l)
echo "$lines"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.