bash 스크립트 인수를 서브 쉘에 전달하는 방법


13

일부 작업을 수행 한 다음 원래 매개 변수를 다른 도구로 전달하는 래퍼 스크립트가 있습니다.

#!/bin/bash
# ...
other_tool -a -b "$@"

"다른 도구"가 서브 쉘에서 실행되지 않는 한 이것은 잘 작동합니다.

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

래퍼 스크립트를 다음과 같이 호출하면

wrapper.sh -x "blah blup"

그런 다음 첫 번째 원래 인수 (-x) 만 "other_tool"로 전달됩니다. 실제로, 나는 서브 쉘을 만들지 않고, 원래의 인자를 안드로이드 폰의 쉘에 전달하는데, 아무런 차이가 없어야한다 :

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"

답변:


14

Bash의 printf명령에는 문자열을 인용 / 이스케이프 / 처리하는 기능이 있습니다. 부모와 하위 셸이 실제로 bash 인 경우 다음과 같이 작동합니다.

[편집 : siegi가 의견에서 지적했듯이, 인수가 제공되지 않은 경우 실제로 하나의 빈 인수가있는 것처럼 작동하는 경우 문제가 발생합니다. 아래에 해결 방법을 추가하여 형식 문자열을로 감싸고 첫 번째 인수가 정의 된 경우${1+} 형식 문자열 만 포함합니다 . 약간 kluge이지만 작동합니다.]

#!/bin/bash

quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"

한 줄로도 할 수 있습니다. bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"


감사합니다! 이것은 나를 위해 아주 잘 작동했습니다.
DribblzAroundU82

인수가 전달되지 않으면 제대로 작동하지 않습니다 (인수가 아닌 하나의 빈 인수를 전달 함).
siegi

1
@siegi 좋은 캐치; 이에 대한 해결 방법을 추가했습니다.
Gordon Davisson

4

어떤 해결책도 잘 작동하지 않습니다. x / \ \ \ "b \"/ aaaaa / \ 'xxx \ yyyy \'/ zz \ ​​"offf \"를 매개 변수로 전달하면 실패합니다.

다음은 모든 경우를 처리하는 간단한 래퍼입니다. 각 인수를 두 번 이스케이프 처리하는 방법에 유의하십시오.

#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
    ARG="$(printf "%q" "$1")"
    ARGS[INDEX]="$(printf "%q" "$ARG")"
    shift
done

ls -l ${ARGS[*]}

1
'/ bin / foo' "$ @" '--opt'와 같이 따옴표 붙은 문자열 인수의 일부로 $ @를 연결하려고 시도하고 예상대로 나오지 않는 경우에도 유용합니다. .
jrg

1
오 감사합니다 이 문제로 어려움을 겪어 보았지만 아무것도 효과가 없었습니다. 이것은 그것을 해결했다. 이 문제가 많이 발생한다는 것을 고려할 때 이중 탈출을 호출하는 bash 내장 기능이 있으면 좋을 것입니다.
MooGoo

2

변경 $@$*. 나는 작은 지역 테스트를했는데 내 경우에는 효과가 있습니다.

#!/bin/sh
bash -c "echo $*"
bash -c "echo $@"

다른 이름으로 저장 test.sh및 실행 가능

$ ./test.sh foo bar
foo bar
foo

보시 다시피 $*와와 의 미묘한 차이가 $@있습니다. 예를 들어 http://ss64.com/bash/syntax-parameters.html 참조


주석의 후속 질문 : 예를 들어 래퍼 로 test.sh수정 된 결합 인수로 구분 기호가있는 문자열을 전달하려면 공백 "두 번"을 이스케이프 처리해야합니다 wc.

#!/bin/sh
bash -c "wc $*"

이것은 작동합니다 :

$ touch test\ file
$ ./test.sh -l "test\ file"
0 test file

그러나:

$ ./test.sh -l "test file"
wc: test: No such file or directory
wc: file: No such file or directory
0 total

불행히도 이것은 작동하지 않습니다. 래퍼를 wrapper.sh -x "blah blup"다음 과 같이 호출하면 서브 쉘이 TWO (-x, "blah blup") 대신 세 개의 매개 변수 (-x, blah, blup)를 얻습니다
Ralf Holly

공백 구분 기호를 유지하려면 인수를 "이중 이스케이프"해야합니다 (예 :) "Blah\ blup". 사용 해보고 작동하는지 확인하십시오.
Daniel Andersson

2
작동하는 것처럼 보이지만 wrapper.sh 호출자가 이스케이프를 추가해야하며 투명한 솔루션을 찾고 있습니다.
Ralf Holly

2

배열 (위치 매개 변수)을 문자열로 강제 변환하기 때문에 실패합니다. "$@"각 개별 매개 변수를 올바르게 인용 된 문자열로 제공하기 때문에 마술입니다. 추가 텍스트를 추가하면 마술이 깨집니다 "blah $@". 단 하나의 문자열입니다.

이것은 당신에게 더 가까이 갈 수 있습니다 :

cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"

물론 작은 따옴표가 포함 된 매개 변수는 문제를 일으킬 수 있습니다.


2

좋아, 더 많은 설명 :

$ cat /tmp/test.sh
#! /bin/bash

echo '$@='"$@"

set -v # "debug"

sh -c 'echo $@' "$@" # gremlins steal the first option

sh -c 'echo $@' -- "$@" # We defend the option with two knifes

$ bash -e /tmp/test.sh first second third
$@=first second third

sh -c 'echo $@' "$@" # gremlins steal the first option
second third

sh -c 'echo $@' -- "$@" # We defend the option with two knifes
first second third

1

나는 많은 다른 솔루션을 시도했지만 배경 정보 및 대안을 포함한 좋은 리소스는 Greg (일명 GreyCat 's) Wiki의 BashFAQ / 096입니다 . 나는 다음 두 가지가 가장 잘 읽을 수 있음을 발견했습니다.


Bash 4.4 이후 ( 뉴스 에서 알 수있는 한 ) 다음 과 같이 매개 변수 확장 을 사용할 수 있습니다 @Q.

adb sh -c "other_tool -a -b ${*@Q}"

전달 된 인수 당 하나의 문자열 대신 하나의 단일 문자열 $*$@되기 때문에 대신 여기에서 사용 합니다 "other_tool -a -b ${*@Q}".

bash 배열 변수 를 사용하여 동일한 작업을 수행 ${ARRAY[*]@Q}하려면 따옴표 안에 구문이 필요합니다 .


당신이 경우 배쉬 4.4 이상 사용할 수없는 또는 확실하지, 이건 내 선호하는 솔루션입니다 :

function escapeBashArgs() {
    local arg separator=""
    for arg
    do
        printf "%s%q" "$separator" "$arg"
        separator=" "
    done
}

adb sh -c "other_tool -a -b $(escapeBashArgs "$@")"

참고 여기에 당신은 사용할 필요가 "$@"대신 $@하거나 "$*"또는 $*(가) 사용할 수 없습니다 인용없이 변형, 그래서 당신은 인수 내에서 단어 분할을하지 않으며, 그렇게, 인수의 수를 보존하기를 원하기 때문에 "$*"이 결합 할 것입니다으로 사용할 수 없습니다 단일 인수에 대한 모든 인수. 그런 다음이 함수는 모든 인수를 단일 문자열로 반환합니다.

첫 번째 인수 앞에 추가 공간이 필요하지 않으면 printf형식 문자열을 변경 " %q"하고 separator변수를 제거 할 수 있습니다 . 또는 Gordon Davissons 의 one-liner를 사용할 수 있습니다 .


이 솔루션은 내가 생각해 낼 수있는 모든 경우에 적용됩니다.

  • 인수 없음 : escapeBashArgs없음
  • 빈 인수 : escapeBashArgs "" ""'' ''
  • 공백이있는 인수 : escapeBashArgs " " " "' ' ' '또는 \ \ \ \ \( 마지막 공간은이 사이트 렌더러가 먹습니다 )
  • 특별한 간격과 줄 바꿈과 인수 : escapeBashArgs "a b" c\ d "arg with newline"'a b' 'c d' $'arg with\nnewline'또는 a\ \ \ \ \ \ b c\ d $'arg with\nnewline'( 줄 바꿈이 사이 withnewline,이 때문에이 사이트에 줄 바꿈의의 다른 위치에 )
  • 특수 문자가 포함 된 인수 : escapeBashArgs '$"'\''({:})''$"'\''({:})'또는\$\"\'\(\{:\}\)
  • jcayzacs 답변의 예 : escapeBashArgs x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"'x/ "b"/aaaaa/'\''xxx yyyy'\''/zz"offf"'또는x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"

(GNU bash 5.0.3 (1)-릴리스로 테스트했습니다.)


-2
#! /bin/bash
sh -c 'other_tool -a -b "$@"' -- "$@"

3
슈퍼 유저에 오신 것을 환영합니다! 우리는 설명과 맥락을 제공하는 긴 답변을 찾고 있습니다. 한 줄만 대답하지 마십시오. 인용문과 함께 답이 옳은 이유를 설명하십시오. 설명이 포함되지 않은 답변은 제거 될 수 있습니다.
G-Man, 'Reinstate
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.