Bash: 루프에 의해 생성된 변수 끝에 쉼표를 제거하는 방법

Bash: 루프에 의해 생성된 변수 끝에 쉼표를 제거하는 방법

같은 줄에 두 개의 난수를 출력하는 다음 bash 스크립트가 있습니다.

#!/bin/bash

for i in 1 2; do
   unset var
   until [ "$var" -lt 10000 ] 2>/dev/null; do
      var="$RANDOM"
done
printf "%s," "${var/%,/}"
done

출력은 다음과 같습니다

5751,2129,

나는 $var = 5751,2129의 출력을 사용할 수 있도록 $var시도한 끝에 있는 쉼표를 제거하려고 합니다 . "${var/%,/}"누구든지 도와줄 수 있나요?

답변1

다음과 같이 할당하면 var="$RANDOM"변수 var는 확장의 문자열을 보유합니다 $RANDOM. Bash에서는 범위 $RANDOM에서 10진수로 확장됩니다 . 거기에는 캐릭터 가 없기 때문에 나중에 확장팩에서 제거하려고 할 필요가 없습니다 .032767,,$var

출력에서 관찰된 쉼표 문자는 코드의 다음 부분에서 나왔습니다.

printf "%s," "${var/%,/}"
# here    ^

이 명령은 루프의 각 반복에서 호출되었으므로 각 반복마다 출력에 하나의 쉼표 문자가 추가되었습니다.

인쇄된 내용은 인쇄 취소할 수 없습니다. 뉘앙스가 있습니다.

  • 출력을 필터로 파이프하면 필터가 출력의 일부를 제거할 수 있습니다. 필터는 스크립트 외부에 있을 수 있습니다(예: 호출 the_script | the_filter). 또는 일부 출력을 파이프할 수 있습니다.부분스크립트 내부의 필터에 스크립트를 추가합니다. 후자의 경우 전체 스크립트의 출력이 필터링됩니다. 여전히 스크립트의 일부에 의해 인쇄된 내용은 인쇄 취소되지 않습니다. 진심이야하다필터에 도착. 필터는 나중에 이를 제거합니다.
  • 터미널로 인쇄하는 경우 이전 출력의 일부를 새 데이터로 덮어쓰도록 할 수 있습니다. 커서를 이동할 수 있는 문자와 시퀀스가 ​​있습니다. 아직도 그들은 모두 터미널에 도착합니다. 이전 출력을 거의 즉시 시각적으로 숨길 수 있지만 출력을 파일(또는 사용된 시퀀스를 이해하지 못하는 터미널)로 리디렉션하면 모든 내용이 거기에 있음을 알 수 있습니다.

원하지 않는 쉼표를 제거하는 올바른 방법은 처음부터 인쇄하지 않는 것입니다. 또는 스크립트 내에서 필터링합니다. 이를 수행하는 방법은 여러 가지가 있으며 모든 방법을 찾는 것은 내 의도가 아닙니다. 나는 그들 중 일부를 논의할 것이다. 두 번 이상의 반복이 있는 루프에 대해서도 가능한 접근 방식을 알고 싶다고 가정합니다. 반복 횟수를 미리 알 수 없는 for 루프일 수도 있습니다. 어쩌면 숫자로 열거되지 않은 for 루프일 수도 있습니다. 아마도 끝나지 않을 수도 있는 for 루프일 수도 있습니다(예: while true대신 for).

참고: 를 사용하면 printf "%s," "${var/%,/}"후행 개행 문자가 인쇄되지 않습니다. 가능하다면 이 동작을 재현하도록 노력하겠습니다.

가능한 접근 방식은 거의 없습니다.

  1. 루프 내부는 에 의존하지 않습니다 $i. 루프를 제거하고 두 개의 별도 변수를 사용할 수 있습니다.

    unset var1
    until [ "$var1" -lt 10000 ] 2>/dev/null; do
       var1="$RANDOM"
    done
    unset var2
    until [ "$var2" -lt 10000 ] 2>/dev/null; do
       var2="$RANDOM"
    done
    printf '%s,%s' "$var1" "$var2"
    

    노트:

    • 그렇지 않다마른.
    • 확장이 잘 되지 않습니다. (만약 당신이 있었다면 for i in {1..100}?)
    • 반복 횟수를 미리 알 수 없으면 번거롭습니다.
  2. 현재 코드를 파이프에 넣고 후행 쉼표를 필터링할 수 있습니다. 예:

    for i in 1 2; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
    printf "%s," "$var"
    done | sed 's/,$//'
    

    노트:

    • sed(또는 사용하는 필터가 무엇이든) 루프가 생성하는 불완전한 줄(개행 문자로 끝나지 않는 줄)을 처리할 수도 있고 처리하지 않을 수도 있습니다. 이 경우 sed구현에 따라 다릅니다.
    • sed처리하는 경우에도 여전히 개행 문자로 출력을 종료할 수 있습니다.
    • sed(또는 사용하는 필터가 무엇이든) 너무 긴 줄을 처리하지 못할 수 있습니다. 위의 특정 코드는 합리적으로 짧은 줄을 생성하지만 일반적으로(많은 반복을 상상해 보십시오) 길이가 문제가 될 수 있습니다.
    • sed는 라인 지향 도구로서 처리하기 전에 전체 라인을 읽어야 합니다. 이 경우 전체 입력을 읽어야 합니다. 모든 반복이 끝날 때까지 아무 것도 얻지 못할 것입니다.
    • 루프는 서브쉘에서 실행됩니다. 일반적으로 어떤 이유로든 메인 쉘에서 작동하기를 원할 수 있습니다.
  3. 현재 코드의 출력을 변수로 캡처할 수 있습니다. 마지막에는 변수를 확장하는 동안 후행 쉼표를 제거합니다.

    capture="$(for i in 1 2; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
    printf "%s," "$var"
    done)"
    printf "%s" "${capture%,}"
    

    노트:

    • 루프는 서브쉘에서 실행됩니다. 일반적으로 어떤 이유로든 메인 쉘에서 작동하기를 원할 수 있습니다.
    • 코드는 마지막 printf(루프 외부)에 도달할 때까지 침묵합니다. 모든 반복이 끝날 때까지 아무 것도 얻지 못할 것입니다.
    • 일반적으로 루프는 원하는 수의 바이트를 출력할 수 있습니다. 나는 Bash가 상당한 양의 데이터를 변수에 저장할 수 있다고 생각합니다. printf은 내장되어 있기 때문에 가능합니다.아마printf "%s" "${capture%,}"때리지 않고 다루다명령줄 길이 제한. 어쨌든 쉘 변수에 많은 양의 데이터를 저장하는 IMO는 최선의 방법이 아니기 때문에 이것을 철저하게 테스트하지 않았습니다. 출력의 길이가 제한되어 있다는 것을 안다면 여전히 연습이 정당화될 수 있습니다. (참고로 위 코드는 확실히 매우 짧은 출력을 생성합니다.)
    • Bash는 NUL 문자를 변수에 저장할 수 없습니다(대부분의 쉘은 할 수 없습니다. zsh는 할 수 있습니다). 또한 $()후행 줄 바꿈을 모두 제거합니다. 이는 변수를 사용하여 데이터를 저장할 수 없음을 의미합니다.임의의출력하고 나중에 정확하게 재현합니다. (참고로 위 코드에서 내부 조각은 $()NUL이나 후행 줄 바꿈을 생성하지 않습니다.)
  4. 출력을 캡처하는 대신 각 반복이 일부 변수에 추가되도록 할 수 있습니다.

    capture=''
    for i in 1 2; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
    capture="$capture$var,"
    done
    printf "%s" "${capture%,}"
    

    노트:

    • 코드는 메인 셸(서브 셸이 아님)에서 실행됩니다.
    • 변수에 데이터를 저장할 때의 제한 사항(이전 방법 참조)은 여전히 ​​적용됩니다.
    • Bash에서는 capture+="$var,". (참고: 변수에 대해 정수 속성이 설정된 경우=+"추가"가 아니라 "추가"를 의미합니다..)
  5. 마지막 반복을 감지하고 다음 없이 형식을 사용할 수 있습니다 ,.

    # this example is more educative with more than two iterations
    for i in {1..5}; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
       if [ "$i" -eq 5 ]; then
          printf "%s" "$var"
       else 
          printf "%s," "$var"
       fi
    done
    

    노트:

    • 서브쉘이 없습니다.
    • 숫자를 미리 모르면 마지막 반복을 감지하는 것이 더 어렵습니다.
    • 배열(예: )을 반복하는 경우 더욱 어렵습니다 for i in "${arr[@]}".
    • 각 반복은 즉시 인쇄되며 순차적으로 출력됩니다. 루프가 무한하더라도 이는 작동합니다.
  6. 첫 번째 반복을 감지하고 ,. 원래 코드 ,%s대신 사용할 수도 있습니다 . %s,그러면 당신은 ,5751,2129대신에 얻을 것입니다 5751,2129,. 이 변경을 통해 후행 쉼표를 피하거나 제거하는 위의 방법 중 하나를 선행 쉼표를 피하거나 제거하는 방법으로 변환할 수 있습니다. 마지막 방법은 다음과 같습니다.

    # this example is more educative with more than two iterations
    for i in {1..5}; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
       if [ "$i" -eq 1 ]; then
          printf "%s" "$var"
       else 
          printf ",%s" "$var"
       fi
    done
    

    노트:

    • 서브쉘이 없습니다.
    • 1항상 시작하는 경우 (또는 일반적으로 고정된 고유 문자열) 첫 번째 반복을 감지하는 것은 쉽습니다 .
    • 하지만 배열을 반복하면 더 어렵습니다 for i in "${arr[@]}". 나중에 배열 if [ "$i" = "${arr[1]}" ]에 동일한 요소가 있을 수 있으므로 확인하면 안 됩니다 . "${arr[1]}"이를 처리하는 간단한 방법은 루프에 대한 인덱스를 유지하고( index=1루프 이전에 매 반복이 끝날 때마다 1씩 증가) 그 값을 1; 하지만 그런 코드는 다소 번거롭다고 생각합니다.
    • 각 반복은 즉시 인쇄되며 순차적으로 출력됩니다. 루프가 무한하더라도 이는 작동합니다.
  7. ,자신을 변수에서 가져오도록 만들 수 있습니다 . 변수가 비어 있는 루프를 입력하고 ,각 반복이 끝날 때마다 변수를 설정합니다. 이렇게 하면 작업이 끝날 때 값이 한 번만 효과적으로 변경됩니다.첫 번째반복. 예:

    # this example is more educative with more than two iterations
    separator=''
    for i in {1..5}; do
       unset var
       until [ "$var" -lt 10000 ] 2>/dev/null; do
          var="$RANDOM"
       done
       printf "%s%s" "$separator" "$var"
       separator=,
    done
    

    노트:

    • 서브쉘이 없습니다.
    • 배열을 반복하는 경우에도 잘 작동합니다.
    • 각 반복은 즉시 인쇄되며 순차적으로 출력됩니다. 루프가 무한하더라도 이는 작동합니다.

    개인적으로 나는 이 방법이 매우 우아하다고 생각합니다.

일반 사항:

  • 각 스니펫은 후행 개행 없이 출력을 생성합니다(필터가 있거나 다른 필터가 있는 경우는 제외 sed). 적절하게 종료된 텍스트 줄을 형성하기 위해 전체 출력이 필요한 경우 루프 다음에 실행 printf '\n'하거나 단독으로 실행하십시오.echo
  • 당신은 ,종결자가 아니라 분리자가 되기를 원합니다. 즉, 반복이 정확히 0개인 루프는 $var반복이 빈 문자열로 확장되는 경우 정확히 1개의 반복이 있는 루프와 동일한 빈 출력을 생성합니다. 우리의 경우에는 $var매번 비어 있지 않은 문자열로 확장되며 반복 횟수가 0회 이상이라는 것을 알고 있습니다. 그러나 일반적인 경우 종결자 대신 구분 기호를 사용하면 모호해질 수 있습니다.

관련 정보