쉘 스크립트에서 백틱 대신 $ ()를 사용하면 어떤 이점이 있습니까?


175

명령 행 출력을 캡처하는 두 가지 방법이 있습니다 bash.

  1. 레거시 Bourne 쉘 백틱 ``:

    var=`command`
  2. $() 구문 (내가 아는 한 Bash에만 해당되거나 최소한 원래 Bourne과 같은 POSIX가 아닌 오래된 쉘에서는 지원되지 않음)

    var=$(command)

백틱과 비교하여 두 번째 구문을 사용하면 어떤 이점이 있습니까? 아니면 두 사람이 완전히 100 % 동일합니까?


14
$()POSIX이며 ksh, bash, ash, dash, zsh, busybox와 같은 모든 최신 Bourne 쉘에서 지원합니다. (그렇게 현대적인 것은 Solaris /bin/sh가 아니지만 Solaris에서는 현대를 /usr/xpg4/bin/sh대신 사용해야합니다 ).
Jens

27
또한 $()별명에서 사용 및 백틱에 대한 참고 사항 입니다. 당신이 가지고있는 경우에 alias foo=$(command)당신의 .bashrc다음 command별칭이 명령하면 실행됩니다 자체가 동안 실행되는 .bashrc해석. 로 alias foo=`command`, command별명이 실행될 때마다 실행됩니다. 그러나 (예 :) 형식으로 $with 를 이스케이프하면 해석하는 대신 별칭이 실행될 때마다 실행됩니다 . 내가 테스트를 통해 알 수있는 한, 어쨌든; bash 문서 에서이 동작을 설명하는 것을 찾을 수 없습니다. $()alias foo=\$(command).bashrc
dirtside

4
@dirtside 어떤 쉘인지, 나는 bash와 POSIX 쉘을 테스트했으며, 소스를 사용할 때 백틱이 실행됩니다. 간단한 예 : alias curDate =`date` 소스를 지정하고 curDate를 실행 한 후 예를 들어 월요일 (월요일) 명령 Mon을 찾을 수 없다는 메시지가 표시됩니다.
thecarpy

@dirtside 사실이 아닙니다. 별명을 사용하더라도 foo = `command` command는 한 번만 실행됩니다. 확인했습니다 : function aaa () {printf date; echo aaa >> ~ / test.txt; } alias test1 = aaa. aaa 함수는 별칭 ( test1)이 몇 번 실행 되었는지에 관계없이 (매번 로그인 한 후) 한 번만 실행됩니다. .bashrc (Debian 10에서)를 사용했습니다.
vitaliydev

나는 5 년 반 전에 내가 사용했던 배쉬 버전이 무엇인지 전혀 모른다. 내 테스트가 잘못되었을 수도 있지만 지금은 어떤 버전을 사용 중인지 의심하기 때문에 중요하지 않습니다.
dirtside

답변:


156

주요한 것은 명령에 명령 을 중첩 시킬 수 있다는 것인데, 어떤 형태의 이스케이프가 백틱에서 작동하는지 알아 내려고 노력하는 정신을 잃지 않고 명령 내에 명령을 내릴 수 있습니다.

예를 들어, 다소 고안되었다.

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

/dir2011 년 12 월 (a) 의 가장 오래된 날짜의 텍스트 파일과 이름이 같은 디렉토리 트리 의 모든 파일 목록이 제공됩니다 .

또 다른 예는 상위 디렉토리의 이름 (전체 경로가 아님)을 얻는 것과 같습니다.

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(a) 이제 특정 명령이 실제로 작동하지 않을 수 있으므로 기능을 테스트하지 않았습니다. 그래서, 당신이 저에게 투표를한다면, 의도를 잃어버린 것입니다. :-) 그것은 버그가없는 프로덕션 레디 스 니펫이 아니라 어떻게 네 스팅을 할 수 있는지에 대한 예시입니다.


87
SO의 모든 코드는 NASA 셔틀 코드 안정성 표준에 맞게 설계된 프로덕션 준비 스 니펫이 될 것으로 기대합니다. 적은 것은 깃발과 삭제 투표를 얻는다.
DVK

1
@DVK 농담이 아닌 경우, 이러한 보증 또는 목적 적합성을 허용하기 위해 SO 기본 (CC-BY-SA 또는 MIT 라이센스)에서 자동으로 라이센스를 취득하지 못한 경우 코드 기여가 표시되어야한다는 데 동의하지 않습니다. 대신에 본인의 위험에 따라 SO의 코드를 재사용하고 도움, 기술적 장점 등에 따라 기고 물에 투표합니다.
chrstphrchvz

9
@chrstphrchvz, 내 프로필을 보면 다음과 같은 작은 스 니펫이 표시됩니다. "스택 오버플로에 게시 한 모든 코드는"원하는대로 수행하십시오 "라이센스에 의해 보호됩니다. 전체 내용은 다음과 같습니다. Do whatever the heck you want with it." :-) 어쨌든 DVK의 유머 였다고 확신합니다.
paxdiablo

1
"cd / home / pax / xyzzy / plover"라면 어떨까요? 꼬인 구절이 다른 미로에서 자신을 발견하겠습니까?
Duncan

@wchargin 당신은이 사건이 C ++ 언어에 사용 된 정의 된 리터럴을 추가하기위한 주요 동기라는 것을 알고 있습니까? en.cppreference.com/w/cpp/language/user_literal
ThomasMcLeod

59

gcc설치된 위치에 해당하는 lib 디렉토리를 찾으려고 가정하십시오 . 선택하실 수 있습니다 :

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

첫 번째는 두 번째보다 쉽습니다. 첫 번째를 사용하십시오.


4
해당 명령 대체에 대한 따옴표를 보는 것이 좋습니다!
Tom Fenech

1
bash의 경우 @TomFenech의 주석은 할당에 적용되지 않습니다. x=$(f); x=`f` 와 동일하게 동작합니다 x="$(f)"; x="`f`". 반대로 배열 할당 x=($(f)); x=(`f`) $IFS 명령을 호출 할 때 예상대로 문자 에서 분할을 수행 합니다. 이것은 편리 x=1 2 3 4하지만 (상당하지 않습니다) 일관성이 없습니다.
kdb

@kdb 당신은 x=$(f)따옴표없이 작업 에 대해 옳습니다 . 나는 더 구체적이어야했다. 나는 libdir=$(dirname "$(dirname "$(which gcc)")")/lib( 내부 명령 대체 주위의 따옴표) 를 사용할 것을 제안했다 . 인용되지 않은 채로 남겨도 여전히 일반적인 단어 분할 및 glob 확장이 적용됩니다.
Tom Fenech

38

백틱 ( `...`)은 가장 오래된 비 POSIX 호환 bourne-shell에만 필요한 레거시 구문 $(...)이며 POSIX이며 몇 가지 이유로 더 선호됩니다.

  • \백틱 내부의 백 슬래시 ( )는 명확하지 않은 방식으로 처리됩니다.

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
  • 중첩 된 인용문 $()이 훨씬 더 편리합니다.

    echo "x is $(sed ... <<<"$y")"

    대신에:

    echo "x is `sed ... <<<\"$y\"`"

    또는 다음과 같은 것을 작성하십시오.

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`

    $()인용을 위해 완전히 새로운 맥락을 사용 하기 때문에

    Bourne 및 Korn 쉘에는 이러한 백 슬래시가 필요하지만 Bash 및 대시는 필요하지 않으므로 이식성이 없습니다.

  • 중첩 명령 대체 구문이 더 쉽습니다.

    x=$(grep "$(dirname "$path")" file)

    보다:

    x=`grep "\`dirname \"$path\"\`" file`

    때문에 $()각 명령의 대체가 보호되고 인용과 이스케이프 이상 특별한 문제없이 자체적으로 처리 할 수 있도록 인용에 대한 완전히 새로운 상황 시행한다. 백틱을 사용하는 경우 레벨이 2 이상이되면 배가 더 나빠집니다.

    몇 가지 더 많은 예 :

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
  • 역 따옴표를 사용할 때 일관되지 않은 동작 문제를 해결합니다.

    • echo '\$x' 출력 \$x
    • echo `echo '\$x'` 출력 $x
    • echo $(echo '\$x') 출력 \$x
  • Backticks 구문에는 포함 된 명령의 내용에 대한 기록 제한이 있으며, 역 따옴표를 포함하는 일부 유효한 스크립트를 처리 할 수 ​​없지만 최신 $()양식은 모든 종류의 유효한 포함 된 스크립트를 처리 할 수 ​​있습니다.

    예를 들어, 유효한 다른 내장 스크립트는 왼쪽 열에서 작동하지 않지만 오른쪽 IEEE 에서는 작동합니다 .

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )

따라서 $-prefixed 명령 대체 구문 은 선호하는 방법이어야합니다. 깨끗한 구문으로 시각적으로 명확하고 (인간 및 기계 가독성 향상) 중첩 가능하고 직관적이며 내부 구문 분석이 분리되어 있으며보다 일관성이 있습니다. 큰 따옴표 내에서 구문 분석되는 다른 모든 확장) (예 : 백틱이 유일한 예외이고 `문자가 인접 해있을 때 문자가 쉽게 위장 "되어 특히 작거나 특이한 글꼴로 읽기가 더 어려워 짐)

출처 : $(...)선호하는 이유는 무엇입니까 `...`(백틱)? BashFAQ에서

또한보십시오:


1
악센트 gravis 스타일 대체의 중첩 인용은 실제로 정의 되어 있지 않으므로 외부 또는 내부에 큰 따옴표 사용할 수 있지만 둘 다를 사용할 수는 없습니다 . 껍질은 그것들을 다르게 해석합니다. 일부는 이스케이프 처리를 위해 백 슬래시가 필요하고 일부는 백 슬래시 이스케이프 처리를 요구하지 않습니다.
mirabilos

23

남자 배쉬에서 :

          $(command)
   or
          `command`

   Bash performs the expansion by executing command and replacing the com-
   mand  substitution  with  the  standard output of the command, with any
   trailing newlines deleted.  Embedded newlines are not deleted, but they
   may  be  removed during word splitting.  The command substitution $(cat
   file) can be replaced by the equivalent but faster $(< file).

   When the old-style backquote form of substitution  is  used,  backslash
   retains  its  literal  meaning except when followed by $, `, or \.  The
   first backquote not preceded by a backslash terminates the command sub-
   stitution.   When using the $(command) form, all characters between the
   parentheses make up the command; none are treated specially.

9

다른 답변 외에도

$(...)

시각적으로 눈에 띄는 것보다

`...`

백틱은 아포스트로피처럼 보입니다. 사용중인 글꼴에 따라 다릅니다.

(방금 알았 듯이 백틱은 인라인 코드 샘플에 입력하기가 훨씬 어렵습니다.)


2
당신은 이상한 키보드가 있어야합니다 (또는 내가합니까?). 나를 위해, 백틱을 입력하는 것이 훨씬 쉽습니다-왼쪽 상단 키이며 Shift 또는 Alt 키가 필요하지 않습니다.
DVK

1
@ DVV : 나는 타이핑이 쉽지 않은 외모에 대해 이야기하고있었습니다. (내 키보드는 아마 당신과 동일합니다.)하지만 지금은 당신이 그것을 언급, 내가 더 나은 근육 기억이 생각 $ (하고 )내가 역 따옴표를 위해 할 이상을; YMMV.
Keith Thompson

bash에서 프로그래밍하지 마십시오 (오래된 ksh에서 Perl로 스킵 됨). 확실히 그 특정 구문에 대한 메모리는 없습니다 :)
DVK

1
@ DVK, 나는 Keith가 비 블록 코드 (여기서 블록 코드는 줄의 시작 부분에 4 개의 공백을 사용함)가 백틱을 사용하여 백틱을 사용하기 어렵다는 사실을 언급한다고 생각했습니다. 중첩 어려움 :-) FWIW, 코드 및 / code 태그 (블록이 아닌 코드를 수행하는 다른 방법)에는 더 쉽게 백틱이 포함될 수 있습니다.
paxdiablo

@Pax-알았습니다. 어이! 나는 어떤 이유로 든 정신적으로 블록 코드에 정신적으로 붙어있었습니다.
DVK

7

$() 중첩을 허용합니다.

out=$(echo today is $(date))

나는 백틱이 그것을 허용하지 않는다고 생각합니다.


2
백틱을 중첩시킬 수 있습니다. 훨씬 더 어렵다 : out=`echo today is \`date\`` .
Jonathan Leffler

4

$(command)명령 대체 형식 을 정의하는 것이 POSIX 표준입니다 . 오늘날 사용되는 대부분의 쉘은 POSIX를 준수하며 기존의 백틱 표기법보다 선호되는 형식을 지원합니다. 쉘 언어 문서 의 명령 대체 섹션 (2.6.3)은 다음을 설명합니다.

명령 대체는 명령 이름 대신 명령 출력을 대체 할 수있게합니다. 명령 대체는 명령이 다음과 같이 묶여있을 때 발생합니다.

$(command)

또는 (backquoted version) :

`command`

쉘 은 서브 쉘 환경에서 명령 을 실행 하고 ( 쉘 실행 환경 참조 ) 명령 대체 ( 명령 텍스트와 "$ ()"또는 역 따옴표)를 명령의 표준 출력으로 대체하여 명령 대체를 확장해야합니다. <newline>대체가 끝날 때 하나 이상의 문자 시퀀스 . <newline>출력이 끝나기 전에 포함 된 문자는 제거되지 않아야합니다. 그러나 IFS의 값과 인용에 따라 필드 구분 기호로 필드 구분 기호로 처리되어 제거 될 수 있습니다. 출력에 널 바이트가 포함되어 있으면 동작이 지정되지 않은 것입니다.

역 인용 된 명령 대체 스타일 내에서 <backslash>' $', ' `'또는 뒤에 오는 경우를 제외하고 문자 그대로의 의미를 유지해야합니다 <backslash>. 일치하는 역 따옴표에 대한 검색은 인용되지 않은 첫 번째 인용되지 않은 역 따옴표에 의해 충족되어야합니다. 이 검색 중에 이스케이프 처리되지 않은 역 따옴표가 쉘 주석, 여기 문서, $ ( command ) 형식 의 내장 명령 대체 또는 인용 된 문자열에서 발견되면 정의되지 않은 결과가 발생합니다. " `...`"시퀀스 내에서 시작하지만 끝나지 않는 작은 따옴표 또는 큰 따옴표 문자열 은 정의되지 않은 결과를 생성합니다.

$ ( command ) 형식을 사용하면 여는 괄호 뒤에 나오는 닫는 괄호 뒤에있는 모든 문자가 command를 구성합니다 . 지정되지 않은 결과를 생성하는 리디렉션만으로 구성된 스크립트를 제외하고 유효한 쉘 스크립트를 command에 사용할 수 있습니다 .

추가 틸드 확장, 파라미터 확장, 명령 대체 또는 산술 확장을 위해 명령 대체의 결과가 처리되지 않아야합니다. 큰 따옴표 안에 명령 대체가 발생하면 대체 결과에 대해 필드 분할 및 경로 이름 확장을 수행하지 않아야합니다.

명령 대체는 중첩 될 수 있습니다. 역 인용 버전 내에서 중첩을 지정하려면 응용 프로그램은 내부 역 인용 부호 앞에 <backslash>문자를 사용해야 합니다. 예를 들면 다음과 같습니다.

\`command\`

쉘 명령 언어의 구문에는 "로 시작하는 확장에 대한 모호성이 있습니다.$((하위 쉘로 시작하는 산술 확장 또는 명령 대체를 도입 할 수 있습니다. 산술 확장이 우선합니다. 즉, 쉘은 먼저 확장을 산술 확장으로 구문 분석 할 수 있는지 여부를 판별하고 확장을 명령으로 구문 분석해야합니다. 확장을 산술 확장으로 구문 분석 할 수없는 것으로 판단되는 경우 쉘은이 결정을 수행 할 때 중첩 된 확장을 평가할 필요가 없습니다. 확장을 산술 확장으로 구문 분석 할 수 없다고 결정하지 않고 입력 종료가 발생하면 쉘은 확장을 불완전한 산술 확장으로 취급하고 구문 오류를보고해야합니다. 적합한 응용 프로그램은 "$( "와 "(하위 쉘로 시작하는 명령 대체에서 두 개의 토큰 (즉, 공백으로 분리)으로 구분됩니다. 예를 들어, 단일 서브 쉘을 포함하는 명령 대체는 다음과 같이 작성 될 수 있습니다.

$( (command) )


4

이것은 레거시 질문이지만 완벽하게 유효한 예를 생각해 $(...)냈습니다 `...`.

cygwin을 실행하는 창에 원격 데스크톱을 사용하고 명령 결과를 반복하고 싶었습니다. 슬프게도, 원격 데스크톱이나 cygwin 자체로 인해 백틱 문자가 입력되지 않았습니다.

이러한 이상한 설정에서 달러 기호와 괄호를 입력하기가 쉽다고 가정하는 것은 당연합니다.

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