bash에게 \n을 변수에 할당하지 않도록 어떻게 알 수 있나요?

bash에게 \n을 변수에 할당하지 않도록 어떻게 알 수 있나요?

다음과 비슷한 변수가 있습니다.

$ echo "$LIST"
file1: ok
file2: ok
file3:
file4:
file5: ok

그런 다음 비정상적인 파일 목록을 가져와야 합니다.

$ sed '/:\s.\+$/d' <<< "$LIST"
file3:
file4:

파일은 유효하지만 목록에 비정상적인 파일이 없으면 다음과 같은 일이 발생합니다.

$ echo "$LIST"
file1: ok
file2: ok
file3: ok
file4: ok
file5: ok
$ sed '/:\s.\+$/d' <<< "$LIST"
$ NEWLIST=$(sed '/:\s.\+$/d' <<< "$LIST")
$ echo "$NEWLIST"

$ cat -A <<< "$NEWLIST"
$
$ wc -l <<< "$NEWLIST"
1
$ wc -c <<< "$NEWLIST"
1

변수에 추가된 이 개행 문자(\n인 것 같습니다)는 wc -l파일이 몇 개 있는지 알기 위해 사용하기 때문에 파일이 나열되는 것으로 식별하기 때문에 내 프로그램을 엉망으로 만듭니다. \n이 bash 또는 sed에 의해 할당되었는지 완전히 확실하지 않습니다. 누구든지 해결책을 알고 있습니까?

답변1

항목 만 제외하려는 경우 ok다음을 수행할 수 있습니다.

grep -cv ': ok$' <<< "$LIST"

또는 비슷하지만 대략 반대입니다.

grep -c ':$' <<< "$LIST"

편집 : @ilkkachu의 의견을 바탕으로

목록이 완전히 비어 있으면 -vc일부 동작으로 인해 변형이 1이라는 개수를 잘못 보고합니다 <<<.

빈 목록을 감지하기 위해 가드를 추가하거나 단순히 파이프를 사용할 수 있습니다.

printf "%s" "$LIST" | grep -vc ": ok$"

목록에 빈 줄이 포함되어 있으면 이를 사용할 때 계산 오류가 발생할 수도 있습니다 -vc.

두 경우 모두 잘못된 계산을 방지하기 위해 추가 수정이 이루어질 수 있습니다.

grep -vcE "^$|: ok$"

그러나 이제 우리는 코드를 이해하기 어렵게 만들기 시작합니다.

답변2

Here-string은 <<<문자열을 명령에 전달하기 전에 문자열 끝에 개행 문자를 추가하는 반면, 명령 대체는 내부 명령의 출력을 읽은 후 후행 개행 문자를 제거합니다.

또한 echo인쇄된 내용에 후행 개행 문자가 추가되지만 여기서는 이것이 주요 문제가 아닙니다.

따라서 변수에 끝에 개행 문자가 있는 완전한 줄이 있다고 가정하면 문자열은 다음과 같습니다.file1: ok<nl>

sed '/ok/d' <<< "$LIST"이제 여기에서 실행 하고 sed입력을 받습니다 file1: ok<nl><nl>. 포함된 줄을 모두 제거 ok하고 빈 줄을 출력합니다 <nl>.

후행 줄 바꿈을 제거하고 빈 문자열을 제공하는 명령 대체를 사용하여 이를 잡을 수 있습니다. 그런 다음 에 할당합니다 NEWLIST.

그런 다음 echo "$NEWLIST"개행 문자가 인쇄되고( echo하나가 추가되었기 때문에) wc -l <<< "$NEWLIST"개행 문자가 입력으로 제공됩니다 wc(here-string에 하나가 추가되었기 때문에).

변수가 원래 just 이고 file1: ok후행 줄 바꿈이 없는 경우 명령 대체만으로는 sed 출력 끝에서 줄 바꿈이 제거되지 않으며 동일한 최종 결과를 얻게 됩니다.

이것이 의미하는 바는 여기서 명령 대체와 문자열이 대부분 한 줄 값으로 작동한다는 것입니다. 변수에 개행 문자가 표시되는 것을 원하지 않을 수도 있지만 일반적으로 명령에 전체 줄을 입력으로 제공하고 싶어한다는 것입니다. 보시다시피, 여러 줄 문자열에도 작동하지만(최종 줄바꿈이 변수에서 다시 누락된 경우) 이상한 줄바꿈 제거 및 추가가 여전히 발생합니다. 제거할 줄바꿈이 없으면 충돌이 발생합니다.

이 저글링을 수행하는 이유를 이해하려면 명령 대체가 here 출력에서 ​​개행 문자를 제거하지 않은 경우 date끝 부분의 마침표 앞에 개행 문자가 인쇄되어 중간에 줄이 끊어진다는 점에 유의하십시오.

$ weekday=$(date +%A)
$ echo "today is a $weekday."
today is a Thursday.

가장 간단한 해결책은 아마도 여러 행의 데이터를 파일에 저장하는 것입니다. 5개 줄이 포함되어 있습니다 inputfile(적절한 줄 바꿈 포함).

file1: ok
file2: ok
file3: ok
file4: ok
file5: ok

그 다음에:

tmpfile=$(mktemp)
sed -e '/ok/d' < inputfile > "$tmpfile"
wc -l < "$tmpfile"
rm -f "$tmpfile"

출력 0.

( 표준 POSIX 기본 또는 확장 정규식 구문이 \s아니거나 둘 다 \+아니므로 macOS와 같은 모든 시스템에서 작동하지 않습니다 sed. 이것이 /ok/위에서 사용한 이유입니다.)

답변3

\n이 bash 또는 sed에 의해 할당되었는지 완전히 확실하지 않습니다.

현재는 둘 다 아닙니다. 예를 들어 확인할 수 있습니다.

$ echo -n "$NEWLIST"  # echo without -n adds a newline
$ wc -l <<< ""
1
$ wc -c <<< ""
1

변수(비어 있음)를 인쇄하고 새 줄로 끝내도록 의도된 것입니다 echo. 이 문자열의 경우 Bash는 마지막 줄이 \n 으로 끝나지 않으면 도구가 이상하게 작동하는 경향이 있기 때문에 새 줄로 끝나는지 확인합니다.

아마도 이 문제를 해결하는 가장 쉬운 방법은 NEWLIST가 비어 있는지 확인하는 것입니다. 또는 파일을 사용하여 보다 직접적으로 작업할 수 있습니다. 예를 들어:

list_file="$(mktemp)"
new_list_file="$(mktemp)"

# cleanup
trap 'rm "$list_file" "$new_list_file"' EXIT

echo "$LIST" > "$list_file"
sed '/:\s.\+$/d' "$list_file" > "$new_list_file"
wc -l "$new_list_file"
# or wc -l < "$new_list_file" if you want to prevent it from printing the filename

예상치 못한 결과의 예로, 여기 문자열에 줄바꿈이 추가되지 않으면 다음 명령의 예상 결과를 고려한 후 실행하세요.

echo -n "Content" | wc -l

대답은 다음과 같습니다: 0

관련 정보