Bash에서 변수를 명령 출력으로 설정하려면 어떻게합니까?


1675

다음과 같은 매우 간단한 스크립트가 있습니다.

#!/bin/bash

VAR1="$1"
MOREF='sudo run command against $VAR1 | grep name | cut -c7-'

echo $MOREF

명령 줄 에서이 스크립트를 실행하고 인수를 전달하면 출력이 표시되지 않습니다. 그러나 $MOREF변수에 포함 된 명령을 실행 하면 출력을 얻을 수 있습니다.

스크립트 내에서 실행해야하는 명령의 결과를 변수에 저장 한 다음 해당 변수를 화면에 출력하려면 어떻게해야합니까?



40
또한 모든 대문자 변수는 운영 체제 나 셸 자체에 의미가있는 변수 이름에 대해 POSIX의해 정의되는 반면, 하나 이상의 소문자가있는 이름은 응용 프로그램 용으로 예약되어 있습니다. 따라서 의도하지 않은 충돌을 피하기 위해 자신의 쉘 변수에 소문자 이름을 사용하는 것이 좋습니다 (쉘 변수를 설정하면 비슷한 환경 변수를 덮어 씁니다).
Charles Duffy

1
변수로 옆으로 캡처 출력으로 너무 그러면 할 echo변수는이다 쓸모 사용 echo, 및 변수의 불필요한 사용.
tripleee

1
추가로, 변수에 출력을 저장하는 것은 종종 불필요합니다. 작고 짧은 문자열의 경우 프로그램에서 여러 번 참조해야합니다. 이것은 완벽하게 잘 진행되는 방법입니다. 그러나 사소한 양의 데이터를 처리하려면 프로세스를 파이프 라인으로 재구성하거나 임시 파일을 사용하려고합니다.
tripleee

답변:


2375

backticks 외에도 `command`, 또는 명령 을 사용하여 명령 대체 를 수행 할 수 있습니다. 읽기 쉽고 중첩 할 수 있습니다.$(command)"$(command)"

OUTPUT=$(ls -1)
echo "${OUTPUT}"

MULTILINE=$(ls \
   -1)
echo "${MULTILINE}"

인용 ( ")은 여러 줄 변수 값 을 보존하는 데 중요 합니다 . 단어 분리가 수행되지 않으므로 과제의 오른쪽에서 선택 사항 이므로 OUTPUT=$(ls -1)제대로 작동합니다.


59
멀티 라인 출력을위한 분리기를 제공 할 수 있습니까?
Aryan

20
공백 (또는 공백 부족)이 중요합니다
Ali

8
@ timhc22에서 중괄호는 관련이 없습니다. 확장 결과가 echo명령 에 전달되기 전에 문자열 분할 및 glob 확장되는지 여부는 중요한 따옴표입니다 .
Charles Duffy

4
아 고마워! 중괄호에 어떤 이점이 있습니까?
timhc22

14
변수 바로 뒤에 변수 이름의 일부로 해석 될 수있는 더 많은 문자가 오는 경우 중괄호를 사용할 수 있습니다. ${OUTPUT}foo . 또한 다음과 같이 변수에 대해 인라인 문자열 연산을 수행 할 때 필요합니다.${OUTPUT/foo/bar}
rich remer

282

올바른 방법은

$(sudo run command)

아포스트로피를 사용하려면 `,하지 말아야 '합니다. 이 문자를 "백틱"(또는 "중음 악센트")이라고합니다.

이처럼 :

#!/bin/bash

VAR1="$1"
VAR2="$2"

MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`

echo "$MOREF"

31
백틱 구문은 더 이상 사용되지 않으므로 실제로 변수 보간을 큰 따옴표로 묶어야합니다 echo.
tripleee

10
위의 과제에서 '='주위의 공백에주의해야한다고 덧붙입니다. 당신은 공백을 shouln't 그렇지 않으면 당신은 잘못된 할당을 얻을 것이다,이
zbstof

4
tripleeee의 의견이 맞습니다. cygwin (2016 년 5 월)에서``작동하는 동안 $()작동 하지 않습니다 . 이 페이지를 볼 때까지 수정할 수 없습니다.
toddwz

2
업데이트 (2018) 의 예와 같은 정교함 이 높이 평가됩니다.
Eduard

90

명령에서 변수를 설정하는 데 사용하는 일부 Bash 트릭

2 차 편집 2018-02-12 : 다른 방법을 추가하여 장기 실행 작업을 위해 맨 아래에서 검색 하십시오 !

2018-01-25 편집 : 샘플 기능 추가 (디스크 사용량에 대한 변수 채우기 )

단순하고 오래되고 호환되는 첫 번째 방법

myPi=`echo '4*a(1)' | bc -l`
echo $myPi 
3.14159265358979323844

대부분 호환되는 두 번째 방법

중첩이 무거워 질 수 있으므로이를 위해 괄호가 구현되었습니다.

myPi=$(bc -l <<<'4*a(1)')

중첩 된 샘플 :

SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted 
1480656334

하나 이상의 변수 읽기 ( Bashisms 사용 )

df -k /
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/dm-0         999320 529020    401488  57% /

사용 된 값을 원한다면 :

array=($(df -k /))

배열 변수를 볼 수 있습니다 :

declare -p array
declare -a array='([0]="Filesystem" [1]="1K-blocks" [2]="Used" [3]="Available" [
4]="Use%" [5]="Mounted" [6]="on" [7]="/dev/dm-0" [8]="999320" [9]="529020" [10]=
"401488" [11]="57%" [12]="/")'

그때:

echo ${array[9]}
529020

그러나 나는 이것을 선호한다 :

{ read foo ; read filesystem size using avail prct mountpoint ; } < <(df -k /)
echo $using
529020

첫 번째 read foo는 헤더 행을 건너 뛰지 만 하나의 명령 에서만 7 가지 변수를 채 웁니다 .

declare -p avail filesystem foo mountpoint prct size using
declare -- avail="401488"
declare -- filesystem="/dev/dm-0"
declare -- foo="Filesystem     1K-blocks   Used Available Use% Mounted on"
declare -- mountpoint="/"
declare -- prct="57%"
declare -- size="999320"
declare -- using="529020"

또는:

{ read foo ; read filesystem dsk[{6,2,9}] prct mountpoint ; } < <(df -k /)
declare -p mountpoint dsk
declare -- mountpoint="/"
declare -a dsk=([2]="529020" [6]="999320" [9]="401488")

... 연관 배열 에서도 작동 합니다 .read foo disk[total] disk[used] ...

일부 변수를 채우기위한 샘플 함수 :

#!/bin/bash

declare free=0 total=0 used=0

getDiskStat() {
    local foo
    {
        read foo
        read foo total used free foo
    } < <(
        df -k ${1:-/}
    )
}

getDiskStat $1
echo $total $used $free

참고 : declare가독성을 위해 줄이 필요하지 않습니다.

sudo cmd | grep ... | cut ...

shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash

(쓸모없는 것을 피하십시오 cat! 그래서 이것은 단지 하나의 포크입니다.

shell=$(grep $USER </etc/passwd | cut -d : -f 7)

모든 파이프 ( |)는 포크를 의미합니다. 다른 프로세스를 실행해야하는 경우 디스크 액세스, 라이브러리 호출 등.

따라서 sed샘플을 사용 하면 하위 프로세스가 하나의 포크로 제한됩니다 .

shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell

그리고 Bashisms :

그러나 대부분 작은 파일에 대한 많은 작업에서 Bash는 작업 자체를 수행 할 수 있습니다.

while IFS=: read -a line ; do
    [ "$line" = "$USER" ] && shell=${line[6]}
  done </etc/passwd
echo $shell
/bin/bash

또는

while IFS=: read loginname encpass uid gid fullname home shell;do
    [ "$loginname" = "$USER" ] && break
  done </etc/passwd
echo $shell $loginname ...

변수 분할 에 대한 추가 정보 ...

Bash의 구분 기호에서 문자열어떻게 분할합니까?에 대한 내 대답을 살펴보십시오 .

대안 : 배경이 긴 장기 실행 작업 을 사용하여 포크 감소

2 차 편집 2018-02-12 :

여러 개의 포크를 방지하기 위해

myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")

또는

myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)

이것은 잘 작동하지만 많은 포크를 실행하는 것은 무겁고 느립니다.

그리고 명령은 라인별로 많은 작업을 수행 date하고 수행 bc할 수 있습니다 !

보다:

bc -l <<<$'3*4\n5*6'
12
30

date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288

따라서 각 요청에 대해 새 포크 를 시작하지 않고도 오래 실행되는 백그라운드 프로세스를 사용하여 많은 작업을 수행 할 수 있습니다 .

이 작업을 제대로 수행하려면 파일 설명자fifo 가 필요 합니다 .

mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc

(물론, FD 56사용되지 않을 수있다!) ..., 당신은이 과정이 사용할 수에서 :

echo "3*4" >&5
read -u 6 foo
echo $foo
12

echo >&5 "pi=4*a(1)"
echo >&5 "2*pi*12"
read -u 6 foo
echo $foo
75.39822368615503772256

기능으로 newConnector

GitHub.Com 또는 내 사이트 에서 내 newConnector기능을 찾을 수 있습니다 (GitHub에 대한 참고 사항 : 내 사이트에는 두 개의 파일이 있습니다. 기능과 데모는 하나의 파일로 묶여 있으며, 소스로 사용하거나 데모 용으로 실행할 수 있습니다.)

견본:

. shell_connector.sh

tty
/dev/pts/20

ps --tty pts/20 fw
    PID TTY      STAT   TIME COMMAND
  29019 pts/20   Ss     0:00 bash
  30745 pts/20   R+     0:00  \_ ps --tty pts/20 fw

newConnector /usr/bin/bc "-l" '3*4' 12

ps --tty pts/20 fw
    PID TTY      STAT   TIME COMMAND
  29019 pts/20   Ss     0:00 bash
  30944 pts/20   S      0:00  \_ /usr/bin/bc -l
  30952 pts/20   R+     0:00  \_ ps --tty pts/20 fw

declare -p PI
bash: declare: PI: not found

myBc '4*a(1)' PI
declare -p PI
declare -- PI="3.14159265358979323844"

이 기능을 myBc사용하면 간단한 구문으로 백그라운드 작업을 날짜별로 사용할 수 있습니다.

newConnector /bin/date '-f - +%s' @0 0
myDate '2000-01-01'
  946681200
myDate "$(ps ho lstart 1)" boottime
myDate now now ; read utm idl </proc/uptime
myBc "$now-$boottime" uptime
printf "%s\n" ${utm%%.*} $uptime
  42134906
  42134906

ps --tty pts/20 fw
    PID TTY      STAT   TIME COMMAND
  29019 pts/20   Ss     0:00 bash
  30944 pts/20   S      0:00  \_ /usr/bin/bc -l
  32615 pts/20   S      0:00  \_ /bin/date -f - +%s
   3162 pts/20   R+     0:00  \_ ps --tty pts/20 fw

거기에서 백그라운드 프로세스 중 하나를 끝내려면 fd 를 닫아야합니다 .

eval "exec $DATEOUT>&-"
eval "exec $DATEIN>&-"
ps --tty pts/20 fw
    PID TTY      STAT   TIME COMMAND
   4936 pts/20   Ss     0:00 bash
   5256 pts/20   S      0:00  \_ /usr/bin/bc -l
   6358 pts/20   R+     0:00  \_ ps --tty pts/20 fw

기본 프로세스가 완료되면 모든 fd가 닫히므로 필요하지 않습니다.


위의 중첩 샘플은 내가 찾던 것입니다. 더 간단한 방법이있을 수 있지만, 내가 찾던 것은 환경 변수에 이름이 지정된 도커 컨테이너가 이미 존재하는지 확인하는 방법이었습니다. 그래서 나를 위해 : EXISTING_CONTAINER=$(docker ps -a | grep "$(echo $CONTAINER_NAME)")내가 찾고있는 진술이었습니다.
염소 자리 1

2
@ capricorn1 쓸모없는 사용이다echo ; 당신은 간단하게grep "$CONTAINER_NAME"
tripleee


아마도 여기에 뭔가가 빠졌을 것입니다 : kubectl get ns | while read -r line; do echo $ line | grep Term | cut -d ''-f1 은 빈 줄 ; done마다 인쇄 $line한 다음 bash: xxxx: command not found. 그러나 나는 그것이 단지 인쇄 될 것으로 예상 할 것이다xxx
파파 니토

77

그들이 이미 지시했듯이 '백틱'을 사용해야합니다.

제안 된 대안 $(command)은 잘 작동하고 읽기도 쉽지만 Bash 또는 KornShell (및 그로부터 파생 된 쉘)에서만 유효하므로 스크립트가 다양한 Unix 시스템에서 실제로 이식 가능 해야하는 경우 선호해야합니다 오래된 백틱 표기법.


23
그들은 매우 신중합니다. 오래 전에 POSIX는 백틱을 더 이상 사용하지 않습니다. 이 밀레니엄의 대부분의 쉘에서보다 현대적인 구문을 사용할 수 있습니다. ( 90 년대 초반에 확고하게 붙어있는 기존 환경의 기침 HP-UX 기침 이 여전히 있습니다 .)
tripleee

25
잘못되었습니다. $()20 년 전에 표준화 된 POSIX sh와 완벽하게 호환됩니다.
Charles Duffy

3
참고 /bin/shSolaris 10에서 여전히 인식하지 않습니다 $(…)너무 솔라리스 11에 사실과 AFAIK -.
Jonathan Leffler

2
그것은 더 이상 실제로 솔라리스 11의 경우 @JonathanLeffler 곳 /bin/sh입니다 ksh93.
jlliagre 2016

2
@tripleee-3 년 늦게 응답 :-) $()지난 10 년 이상 HP-UX의 POSIX 셸에서 사용 했습니다.
밥 자비스-복원 모니카

54

나는 세 가지 방법을 알고 있습니다.

  1. 이러한 작업에는 기능이 적합합니다. **

    func (){
        ls -l
    }

    라고 말하여 호출하십시오 func.

  2. 또한 다른 적절한 솔루션은 평가가 될 수 있습니다.

    var="ls -l"
    eval $var
  3. 세 번째는 변수를 직접 사용하는 것입니다.

    var=$(ls -l)
    
        OR
    
    var=`ls -l`

세 번째 솔루션의 출력을 좋은 방법으로 얻을 수 있습니다.

echo "$var"

그리고 불쾌한 방법으로 :

echo $var

1
처음 두 가지는 현재의 질문에 대답하지 않는 것 같고, 두 번째는 일반적으로 모호한 것으로 여겨집니다.
tripleee

1
강타를 완전히 처음 접하는 사람으로서 왜 "$var"선하고 $var불쾌합니까?
Peter


30

다른 것 :

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)

22

변수를 설정할 때 = 부호 앞뒤에 공백이 없어야 합니다. 말 그대로 한 시간 동안 이것을 알아 내려고 모든 종류의 솔루션을 시도했습니다! 이것은 시원하지 않습니다.

옳은:

WTFF=`echo "stuff"`
echo "Example: $WTFF"

"stuff : not found"또는 이와 유사한 오류로 실패

WTFF= `echo "stuff"`
echo "Example: $WTFF"

2
공간을 가진 버전은 뭔가 다른 의미 : var=value somecommand실행 somecommandvar환경이 값을 갖는 년을 value. 따라서 빈 (0 바이트) 값 을 가진 환경에서 var= somecommand내보내고 있습니다. varsomecommand
찰스 더피

그렇습니다, 배쉬가있어.
Peter Mortensen

14

multiline / multiple command / s로 수행하려면 다음을 수행하십시오.

output=$( bash <<EOF
# Multiline/multiple command/s
EOF
)

또는:

output=$(
# Multiline/multiple command/s
)

예:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

산출:

first
second
third

heredoc을 사용하면 긴 한 줄 코드를 여러 줄로 나눠서 일을 쉽게 단순화 할 수 있습니다. 또 다른 예:

output="$( ssh -p $port $user@$domain <<EOF
# Breakdown your long ssh command into multiline here.
EOF
)"

2
두 번째 bash명령 대체는 무엇입니까? 명령 대체 자체로 이미 서브 쉘을 작성 중입니다. 여러 명령을 넣으려면 줄 바꿈 또는 세미콜론으로 구분하십시오. output=$(echo first; echo second; ...)
tripleee

그런 다음 마찬가지로 'bash -c "bash -c \"bash -c ...\""'"다른"것입니다; 그러나 나는 그 요점을 보지 못합니다.
tripleee

@tripleee heredoc은 그 이상을 의미합니다. ssh sudo -s내부에서 mysql 명령을 실행 하는 것과 같은 다른 명령으로도 같은 작업을 수행 할 수 있습니다 . (bash 대신)
Jahid

1
나는 우리가 제대로 의사 소통하고 있다고 생각하지 않습니다. 나는 이상 유용성에 도전하고 variable=$(bash -c 'echo "foo"; echo "bar"')이상 variable=$(echo "foo"; echo "bar")- 여기에 문서가 단지 인용 메커니즘입니다 정말 다른 쓸모없는 합병증을 제외하고 아무것도 추가하지 않습니다.
tripleee

2
heredoc을 ssh와 함께 사용하면 경고 ssh -p $port $user@$domain /bin/bash <<EOF를 방지하기 위해 실행할 명령이 정확합니다.Pseudo-terminal will not be allocated because stdin is not a terminal.
F. Hauri

9

둘 중 하나를 사용해야합니다

$(command-here)

또는

`command-here`

#!/bin/bash

VAR1="$1"
VAR2="$2"

MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"

echo "$MOREF"


1
나는 네가 둥지를 지을 수 있다는 것을 몰랐지만 그것은 완벽하게 이해된다. 정보에 대해 대단히 감사합니다!
Diego Velez

6

이것은 또 다른 방법이며 모든 복잡한 코드를 정확하게 강조 표시 할 수없는 일부 텍스트 편집기와 함께 사용하는 것이 좋습니다.

read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"

이것은 프로세스 대체가 아니라 명령 대체 에 관한 OP의 질문을 다루지 않습니다 .
codeforester

6

백틱 (악센트 무덤이라고도 함)을 사용하거나 $() .

처럼:

OUTPUT=$(x+2);
OUTPUT=`x+2`;

둘 다 같은 효과가 있습니다. 그러나 OUTPUT = $ (x + 2)는 더 읽기 쉽고 최신입니다.


2
중첩을 허용하기 위해 괄호가 구현되었습니다.
F. Hauri

5

실행하려는 명령이 실패하면 출력을 오류 스트림에 기록한 다음 콘솔에 인쇄합니다.

이를 방지하려면 오류 스트림을 경로 재지 정해야합니다.

result=$(ls -l something_that_does_not_exist 2>&1)

4

일부는이 기능이 유용 할 수 있습니다. 트릭이 $(())이중 괄호를 사용하는 변수 대체의 정수 값 :

N=3
M=3
COUNT=$N-1
ARR[0]=3
ARR[1]=2
ARR[2]=4
ARR[3]=1

while (( COUNT < ${#ARR[@]} ))
do
  ARR[$COUNT]=$((ARR[COUNT]*M))
  (( COUNT=$COUNT+$N ))
done

1
이것은이 질문과 관련이없는 것 같습니다. 누군가가 배열의 숫자에 일정한 인수를 곱하는 방법을 묻는다면 합리적인 대답이 for ((...))될 것입니다. ). 또한 개인 변수에 대문자를 사용해서는 안됩니다.
tripleee

나는 "관련성"부분에 동의하지 않습니다. Bash의 명령 출력과 동일한 변수를 설정하는 방법은 무엇입니까? 그리고 나중에이 코드를 게시하는 데 도움이되는 솔루션을 찾고 있기 때문에이 답변을 보완으로 추가했습니다. 대문자 변수에 관해서는 감사합니다.
거스

1
이 글을 쓸 수는 ARR=(3 2 4 1);for((N=3,M=3,COUNT=N-1;COUNT < ${#ARR[@]};ARR[COUNT]*=M,COUNT+=N)){ :;}있지만 @tripleee에 동의합니다.이 작업을 이해하지 못합니다!
F. Hauri

@ F.Hauri ... bash는 더 깊이 들어가면 perl과 비슷해집니다!
roblogic

4

두 가지 방법이 더 있습니다 :

Bash에서 공간은 매우 중요합니다. 따라서 명령을 실행하려면 공백을 추가하지 않고있는 그대로 사용하십시오.

  1. 양수인 다음 harshil에를 L다음과하면을 인쇄

    L=$"harshil"
    echo "$L"
  2. 다음은 명령의 출력 tr을 L2에 할당합니다 . tr다른 변수 L1에서 작동하고 있습니다.

    L2=$(echo "$L1" | tr [:upper:] [:lower:])

4
1. $"..."아마 당신이 생각하는 것을하지 않습니다 . 2. 이것은 Andy Lester의 답변에 이미 나와 있습니다.
gniourf_gniourf

@gniourf_gniourf가 옳다 : bash 현지화가 여러 줄에서 작동하지 않는 것을 참조하십시오 . 그러나 bash 에서는 echo ${L1,,}소문자 또는 대문자로 사용할 수 있습니다 echo ${L1^^}.
F. Hauri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.