쉘 스크립트가 쉘 프롬프트에서 작성할 때 인용 된대로 인수를 인쇄 할 수 있습니까?


9

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

gcc -fPIC "$@"

bash는 패스 - 투 - 표준 입력 (stdin) 구문을 사용하면 <<<생각을, "@$"나는 그것을 기대하는대로 작동하지 않습니다.

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

로 스크립트를 호출 ./test.sh foo "bar baz"제공

foo bar baz

나는 기대할 것이다

foo "bar baz"

쉘 프롬프트에서 작성할 때 인수를 인쇄하는 쉘 스크립트를 작성하는 방법이 있습니까? 예 : 힌트에 스크립트 인수를 포함하여 다음에 사용할 명령에 대한 힌트입니다.

답변:


5

자, "$@"위치 매개 변수 목록으로 확장합니다. 위치 매개 변수 당 하나의 인수입니다.

할 때 :

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

cmd빈 문자열 foo bar및 이라는 세 가지 인수로 호출됩니다 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위치 매개 변수를 인수로 실행했습니다. 내 껍질은 껍질에 다시 입력하기에 적합한 인용 된 방식으로 인쇄했습니다. 모든 껍질이 그런 것은 아닙니다.


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

아니요, 이것은 일어나지 않습니다. 프로그램을 호출하면 인수 목록 이 사용되며 각 인수는 문자열입니다. 당신은 쉘 프로그램을 실행하면 ./test.sh foo "bar baz":이 세 가지 인수 전화를 구축 ./test.sh, foobar baz. (제 0의 인수는 프로그램 명입니다. 이것은 프로그램이 어떤 이름을 호출하는지 알 수있게합니다.) 인용은 프로그램 호출의 기능이 아니라 쉘의 기능입니다. 쉘은 호출 할 때이 목록을 작성합니다.

"$@"스크립트 또는 함수에 전달 된 인수 목록을 사용 된 호출의 인수 목록에 직접 복사합니다. 해당 목록에서 쉘 구문 분석이 수행되지 않으므로 인용이 없습니다.

에서가 cat <<< "$@", 당신이 사용하는 "$@"하나의 문자열이 요구되는 상황에서. <<<operator`가 아닌 문자열 문자열 목록이 필요합니다. 이와 관련하여 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

2

"$@" 스크립트 인수로 확장하여 필요에 따라 인용

글쎄요. 실질적인 목적을 위해 충분히 가까이 있어야하며, 참고 설명서 는 다음 과 같이 말합니다."$@" 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로 끝나는 문자열 목록을 수신하고 따옴표는 쉘 언어의 기능 일뿐입니다. 다른 언어에는 다른 규칙이있을 수 있습니다.


0

bash에 대한 대체 솔루션

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

Bash는 중첩 대체를 지원하지 않으므로 /programming/12303974/assign-array-to-variable#12304017 덕분에 배열을 다시 할당하는 방법을 보여주었습니다. 배열, 확장 및 패턴 대체 (매개 변수 확장)에 대한 자세한 내용 은 man bash( https://linux.die.net/man/1/bash )를 참조하십시오 .

분석

Bash는 명령 행 매개 변수를 배열로 넣습니다. $@

q 인용 문자를 보유합니다.

매개 변수 확장을 큰 따옴표로 묶으 ${ ... }면 개별 매개 변수를 개별 요소로 유지하고이를 래핑 ( )하면 변수에 배열로 지정할 수 있습니다.

/#/$q매개 변수 확장에서 패턴의 시작 (예 : regex ^)을 인용 문자로 대체합니다 .

/%/$q매개 변수 확장에서 패턴의 끝 (예 : regex $)을 따옴표로 대체합니다 .

유스 케이스 : 명령 행에서 이메일 주소 목록을 위해 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

큰 따옴표로 인용하면 백 슬래시, $확장 및 기타 큰 따옴표 를 보호하는 데별로 도움이되지 않습니다 . 그렇기 때문에 다른 답변이 작은 따옴표를 사용하는 동안 문자열 에서 작은 따옴표를 처리 하거나 따옴표로 묶은 문자열 복사본을 생성하기 위해 쉘 자체의 기능을 사용합니다.
ilkkachu 2016 년

@ilkkachu Duly는 언급했다! 그렇기 때문에 (다른 이유로) 이전의 모든 답변을 찬성했습니다. 또한이 유스 케이스를 추가 한 이유. 바라건대 완전은 선의 적이 아닙니다.
Jeff
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.