bash의 대체 솔루션

bash의 대체 솔루션

"$@"쉘 스크립트에서는 필요에 따라 스크립트 매개변수를 인용하여 확장하는 것으로 이해됩니다 . 예를 들어 다음은 스크립트 매개변수를 gcc로 전달합니다.

gcc -fPIC "$@"

<<<그러나 bash pass-to-stdin 구문을 사용하면 "@$"예상대로 작동하지 않습니다.

#!/bin/bash
cat <<< "$@"

./test.sh foo "bar baz"주어진 대로 스크립트를 호출하세요.

foo bar baz

나는 희망

foo "bar baz"

마치 쉘 프롬프트에서 작성된 것처럼 인수를 인쇄하는 쉘 스크립트를 작성하는 방법이 있습니까? 예: 힌트의 스크립트 매개변수를 포함하여 다음에 사용할 명령에 대한 힌트입니다.

답변1

"$@"위치 인수당 하나의 인수로 구성된 위치 인수 목록으로 확장하세요 .

이 작업을 수행할 때:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmd호출은 빈 문자열 foo bar및 3개의 매개변수를 사용하여 수행됩니다 blah<newline>blah. 쉘은 execve()다음과 같이 시스템 호출을 호출합니다.

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

동일한 호출을 재현하기 위해 셸 명령줄(즉, 셸 언어의 코드)을 재구성하려면 다음을 수행할 수 있습니다.

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

또는 을 사용하여 zsh다양한 유형의 견적을 요청하세요.

$ set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

또는 zsh, bash또는 ksh93(여기서는 bash다른 쉘과 함께 YMMV)을 사용하십시오.

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

또한 쉘의 xtrace 옵션을 사용하여 쉘이 실행하려는 내용을 인쇄하도록 할 수도 있습니다:

$ (PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

:위에서는 위치 인수를 인수로 사용하여 no-op 명령을 실행했습니다 . cmd내 쉘은 쉘에 다시 입력하기에 적합한 멋진 인용 방식으로 이를 인쇄합니다. 모든 쉘이 이 작업을 수행하는 것은 아닙니다.

답변2

`"$@"` expands to the script arguments, quoting them as needed

아니요, 그렇지 않습니다. 프로그램을 호출하려면목록매개변수, 각 매개변수는 문자열입니다. 쉘 프로그램을 실행하면 , 및 3개의 매개변수를 사용 하여 ./test.sh foo "bar baz"호출이 구성됩니다 . (0번째 인수는 프로그램 이름입니다. 이를 통해 프로그램은 호출되는 이름을 알 수 있습니다.) 인용은 프로그램 호출의 기능이 아니라 쉘의 기능입니다. 쉘은 호출할 때 이 목록을 작성합니다../test.shfoobar baz

"$@"스크립트나 함수에 전달된 인수 목록을 이를 사용하는 호출의 인수 목록에 직접 복사합니다. 이러한 목록은 쉘 구문 분석되지 않으므로 인용이 포함되지 않습니다.

In에서는 cat <<< "$@"단일 문자열이 예상되는 컨텍스트에서 사용합니다. "$@"연산자는 <<<문자열 목록이 아닌 문자열을 기대합니다. 이 경우 bash는 목록의 요소를 가져와서 공백으로 연결합니다.

set -x스크립트 디버깅의 경우 ( 닫기 위해) 실행하면 set +x실행 전에 각 명령이 인쇄되는 추적 모드가 활성화됩니다. Bash에서는 추적이 인용되고 명령을 셸에 다시 붙여넣을 수 있습니다(모든 sh구현에서 이 작업을 수행하는 것은 아닙니다).

문자열이 있고 이를 셸 소스 구문으로 변환하고 다시 원래 문자열로 구문 분석하려는 경우 이를 작은따옴표로 묶고 문자열의 각 작은따옴표를 로 바꿀 수 있습니다 '\''.

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

문자열 대체 구문은 ksh93/bash/zsh/mksh에만 해당됩니다. 일반 sh에서는 문자열을 반복해야 합니다.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

답변3

"$@"필요에 따라 인용하여 스크립트 매개변수로 확장합니다.

글쎄요. 실용적인 목적을 위해 충분히 가까워야 하며,참고 도서실제로 그렇게 말했다"$@" is equivalent to "$1" "$2" ...

따라서 두 개의 인수를 사용하면 다음 foobar baz유사합니다.

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(인수에 일반 문자열뿐만 아니라 특수 문자가 포함된 경우를 제외하고는 확장 후 다시 확장되지 않습니다 $@... $1)

그러나 따옴표로 매개변수 대체를 고려하더라도 따옴표를 얻지 못하는 것처럼 $@따옴표가 나타나지 않습니다 .echogcc

<<<"$@"== 규칙의 예외 "$1" "$2" ...이며 명시적입니다.언급하다The result is supplied as a single string to the command on its standard input매개변수 및 변수 확장, 따옴표 제거 등을 거친 후 따라서 평소와 같이 입력으로 <<< "foo"만 제공되고 foo동일한 방식으로 인수로만 somecmd "foo"제공됩니다 foo.

./test.sh foo "bar baz"나는 스크립트가 호출 될 것으로 기대하고 있습니다 [...]foo "bar baz"

제안이 유지되는 경우에도 여전히 이어야 합니다 "foo" "bar baz". 쉘이나 실행 중인 명령은 실행 시 명령이 참조되는 내용을 알지 못합니다. 또는 언급할 따옴표가 있더라도 시스템 호출은 null로 끝나는 문자열 목록을 수신하며 따옴표는 셸 언어의 기능일 뿐입니다. 다른 언어에는 다른 규칙이 있을 수 있습니다.

답변4

bash의 대체 솔루션

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bash는 중첩 대체를 지원하지 않으므로 감사합니다.https://stackoverflow.com/questions/12303974/sign-array-to-variable#12304017배열을 재할당하는 방법을 보여 주는 데 사용됩니다. 바라보다 man bash(https://linux.die.net/man/1/bash) 배열, 확장 및 패턴 대체에 대한 자세한 내용은 (매개변수 확장 아래)

분석하다

Bash는 명령줄 인수를 배열로 넣습니다.$@

q인용된 문자를 저장합니다.

매개변수 확장에 큰따옴표를 사용하면 ${ ... }개별 매개변수를 고유한 요소로 보존하고 이를 묶으면 ( )변수에 배열로 할당할 수 있습니다.

/#/$q^매개변수 확장에서 패턴의 시작 부분을 따옴표로 묶은 문자(예: 정규식)로 바꿉니다.

/%/$q$매개변수 확장에서 패턴의 끝을 따옴표로 묶은 문자(예: 정규식)로 바꿉니다.

사용 사례: 명령줄에서 MySQL에 이메일 주소 목록을 쿼리합니다.

위의 명령문에는 다른 따옴표 문자를 사용하고, 매개변수 사이에 쉼표를 추가하고, 마지막 쉼표를 제거하는 등 몇 가지 변형이 있습니다. 물론 mysql 호출시 비밀번호를 잘못 입력했습니다. 그러니 나를 고소하세요.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END

관련 정보