Bash 옵션을 사용하면 명령 대체에서 후행 개행이 유지됩니다.

Bash 옵션을 사용하면 명령 대체에서 후행 개행이 유지됩니다.

bash명령 교체로 인해 후행 개행 문자가 제거되는 것을 방지하는 옵션이 제공되었으면 좋겠습니다 . 당신은 있나요? 그렇지 않다면 POSIX와 같은 쉘이 있습니까 bash? 표준(POSIX인 것 같습니다) sh코드를 실행하는 쉘이 있습니까? 그런 옵션이 있거나 개행 문자를 제거하지 않는 특수 명령 대체 구문이 있을 수 있습니다(후자가 더 좋습니다). )?

답변1

(내가 아는 한) 이 작업을 직접 수행할 수 있는 옵션은 없지만 명령 대체에 개행이 아닌 보호 문자를 추가한 다음 제거하여 가짜로 만들 수 있습니다.

var="$(somecommand; echo .)"  # Add a "." (and one newline that command
                              # substitution will remove) *after* the
                              # newline(s) we want to protect

var="${var%.}"  # Remove the "." from the end

과 의 종료 상태는 둘 다 상태가 설정되어 있기 somecommand때문에(보통 0) 프로세스에서 손실됩니다 . 끝까지 저장해야 한다면 저글링을 추가해야 합니다(Kusalananda 제공).echo .var=${var%.}

var="$(somecommand; err=$?; echo .; exit "$err")"  # Make the subshell
                                                   # exit with somecommand's
                                                   # status
commandstatus=$?    # Preserve the exit status of the subshell
var="${var%.}"
# You can now test $commandstatus to see if the command succeeded

err및 변수 는 모두 commandstatus명령의 종료 상태를 저장하지만 및 err에 의해 생성된 하위 쉘에만 존재 $( )하고 commandstatus상위 쉘에만 존재하므로 중복되지 않습니다. 둘 다에 동일한 이름을 사용할 수 있지만 이로 인해 혼란이 가중될 수 있습니다.

그런데, "." 뒤에 상태를 보존할 필요가 없다면 잘려서 이 commandstatus부분은 건너뛸 수 있습니다.

if var="$(somecommand; err=$?; echo .; exit "$err")"; then
    # somecommand succeeded; proceed with processing
    var="${var%.}"
    dosomethingwith "$var"
else
    echo "somecommand failed with exit status $?" >&2
fi
로케일에 따라 "." 대신 어떤 문자도 사용할 수 없습니다. POSIX는 "." 인코딩이 다른 문자 인코딩에서 발견되지 않도록 보장하지만 모든 경우에 해당되는 것은 아닙니다. 예를 들어 실제로 "x"의 인코딩은 BIG5 또는 GB18030 인코딩에서 많은 문자의 인코딩 끝에 있으므로 "." 대신 "x"를 추가하면 "x"는 다음과 같이 끝날 수 있습니다. 출력의 끝에 나타나는 다른 바이트 조합은 새로운 문자를 형성하며 "${var%x}"는 "x"를 제거할 수 없습니다.

답변2

내가 아는 한, 명령 대체 기능이 있고 후행 개행 문자를 제거하지 말라고 지시할 수 있는 유일한 쉘은 rc(Unix V10 및 plan9의 쉘, Byron Rakitzis의 공개 도메인 복제 및 es또는 그 파생물 akanga) 및 입니다 fish.

rc쉘과 같은 상황 에서 ,

var = `{somecommand}

$ifs출력에서 구분된 단어를 somecommand변수에 저장합니다 $var(변수는 의 배열/목록임 rc).

비어 있으면 $ifs( ifs = ()) 출력이 그대로 저장됩니다. 구분 기호를 지정할 수도 있습니다 ``(separators){somecommand}. 즉, 다음 과 같습니다.

var = ``(){somecommand}

하지만 rc//The Bourne Supremacy와는 전혀 다릅니다 es.akanga

또한 일반적으로 제거하고 싶은 경우하나개행 문자.

예를 들어:

dirname=$(dirname -- "$file")

추가한 줄 바꿈을 제거하고 싶지만 dirname디렉터리 끝에 있을 수 있는 줄 바꿈은 제거하고 싶지 않습니다.$file

rc내장된 연산자가 없습니다. 다음을 수행해야 합니다.

dirname = ``(){dirname -- $file | head -c -1}

(표준에는 없습니다 head). 패턴 추출 연산자를 es사용할 수 있습니다 .~~

nl = '
'
dirname = <={ ~~ ``(){dirname -- $file} *$nl }

이것은 거의 명확하지 않습니다(비록 함수로 만들 수는 있지만).

fish명령 대체는 출력을 해당 구성 요소 줄로 분할합니다.

set var (somecommand)

출력 라인을 somecommand배열에 저장합니다 $var(출력 끝에 있는 빈 라인도 유지됩니다).

빈 목록이나 빈 문자열로 설정하면 $IFS해당 분할이 비활성화되고 최대 하나의 줄 바꿈이 출력 끝에서 제거됩니다(적어도 fish현재 버전 IIRC에서는 이와 관련하여 몇 가지 변경 사항이 있습니다. 년) . 그래서 거기,

begin; set -l IFS; set dir (dirname -- $file); end

신뢰할 수 있습니다.

나는 벗기지 말라고 할 수 있는 Bourne 같은 껍질을 모른다.모두후행 개행 문자. 이 동작은 POSIX에서 필요하며 Bourne과 유사한 모든 쉘은 posix 모드가 아닌 경우에도 이를 수행합니다(bash 또는 zsh와 같은 쉘을 사용하는 경우).

4.4+ 에서는 명령 대체와 함께 프로세스 대체를 사용할 수도 있습니다(또는 bash비대화형 인스턴스에서 readarray및 옵션이 있는 파이프 ).lastpipe

readarray -td '' var < <(somecommand)

이것은 -d ''NUL의 분할입니다. bash어쨌든 변수에 NUL을 저장하는 것은 지원되지 않으므로 명령 대체의 경우 대체만으로 충분합니다.

readarray -t lines < <(somecommand)

출력 행을 배열 somecommand에 저장합니다. $lines그런 다음 개행 문자로 연결하여 출력을 재구성할 수 있습니다.하나후행 개행:

IFS=$'\n'
output="${lines[*]}"

그러나 이러한 모든 방법에서는 종료 상태가 손실됩니다 somecommand.

명령 대체에 의해 제거된 잘못된 기능을 해결하려면모두개행 문자의 일반적인 관용어는 개행 문자가 아닌 문자를 추가하고 이를 제거하는 것입니다(그리고하나개행 문자) Gordon이 말했듯이.

그러나 실존적 지위를 잃지 않고 이를 수행하려면 다음과 같이 하십시오.

cmdsubst() { # args: var cmd args
  eval "$1"'=$(shift; "$@"; ret=$?; printf .; exit "$ret")'
  eval "$1=\${$1%.}; $1=\${$1%'
'}; return $?"
}

이는 명령 대체와 유사합니다.하나개행 문자가 제거됩니다.

다음과 같이 사용됩니다:

cmdsubst dir dirname -- "$file"

바꾸다:

dir=$(dirname -- "$file")

또는 더 복잡한 명령의 경우:

cmdsubst var eval 'cmd1; for i in a b; do cmd "$i"; done'

답변3

많은 사용 사례에서 작동하는 간단한 접근 방식은 명령 대체를 큰따옴표로 묶고 닫는 따옴표 바로 앞에 개행 문자를 삽입하는 것입니다. 개행 문자를 배치하여~에인용하지만외부괄호를 사용하면 명령 대체 범위에서 이를 제거하고 쉘은 이를 자동으로 제거하지 않습니다.

첫 번째 예에서는 닫는 따옴표 앞에 개행 문자가 없으므로 다음 $프롬프트는 출력 직후에 나타납니다. 두 번째 예제가 작동합니다.

$ printf '%s' "$(date)"
Mon Jan 22 13:39:23 PST 2024$ printf '%s' "$(date) 
"
Mon Jan 22 13:39:26 PST 2024
$

이 방법에는 쉘이 괄호로 묶인 명령 대체에서 후행 줄 바꿈을 제거하고 이후에 스크립트가 하나의 줄 바꿈만 추가하기 때문에 남아 있다는 제한이 있습니다.단 하나후행 개행 문자. 개행 문자가 여러 개 있으면 하나로 압축됩니다.

관련 정보