변수에 저장된 명령을 어떻게 실행할 수 있습니까?

$ ls -l /tmp/test/my\ dir/ total 0 

위 명령을 실행하는 다음 방법이 왜 실패하거나 성공하는지 궁금합니다.

$ abc="ls -l "/tmp/test/my dir"" $ $abc ls: cannot access ""/tmp/test/my": No such file or directory ls: cannot access "dir"": No such file or directory $ "$abc" bash: ls -l "/tmp/test/my dir": No such file or directory $ bash -c $abc "my dir" $ bash -c "$abc" total 0 $ eval $abc total 0 $ eval "$abc" total 0 

댓글

답변

이것은 unix.SE의 여러 질문에서 논의되었습니다. 여기서 생각해 낼 수있는 문제입니다. 마지막에는 참조가 있습니다.


실패한 이유

이러한 문제에 직면 한 이유는 단어 분할 및 변수에서 확장 된 따옴표는 따옴표로 작동하지 않고 일반 문자 일뿐입니다.

질문에 제시된 사례 :

여기서 할당은 단일 문자열 –abc :

$ abc="ls -l "/tmp/test/my dir"" 

아래, $abc는 공백으로 분할되고 ls는 세 개의 인수 -l, "/tmp/test/mydir" (두 번째 앞에 인용 부호가 있고 세 번째 뒤에 다른 인용 부호가 있음). 옵션은 작동하지만 경로가 잘못 처리됩니다.

$ $abc ls: cannot access ""/tmp/test/my": No such file or directory ls: cannot access "dir"": No such file or directory 

여기서 확장은 따옴표로 묶여 있으므로 단일 단어로 유지됩니다. 문자 그대로 ls -l "/tmp/test/my dir"라는 프로그램을 찾으려면 공백과 따옴표가 포함되어 있습니다.

$ "$abc" bash: ls -l "/tmp/test/my dir": No such file or directory 

그리고 여기에서는 $abc가 분할되고 첫 번째 결과 단어 만 -c의 인수로 사용되므로 Bash는 ls 다른 단어는 bash에 대한 인수이며 $0, $1 등을 채우는 데 사용됩니다.

p>

$ bash -c $abc "my dir" 

bash -c "$abc"eval "$abc"를 사용하면 쉘 처리 단계는 따옴표가 작동하도록하지만 또한 모든 쉘 확장이 다시 처리되도록합니다 . 따라서 “예를 들어 사용자가 제공 한 데이터에서 명령을 대체하는 등 실수로 실행할 위험이 있습니다.” 아주 조심해


더 나은 방법

명령을 저장하는 더 좋은 두 가지 방법은 a) 대신 함수를 사용하고 b) 배열 변수를 사용하는 것입니다 ( 또는 위치 매개 변수).

함수 사용 :

간단히 선언 내부에 명령이있는 함수를 사용하고 명령 인 것처럼 함수를 실행합니다. 함수 내의 명령 확장은 명령이 정의 된 경우가 아니라 실행될 때만 처리되며 개별 명령을 인용 할 필요가 없습니다.

# define it myls() { ls -l "/tmp/test/my dir" } # run it myls 

배열 사용 :

배열을 사용하면 개별 단어에 흰색이 포함 된 여러 단어 변수를 만들 수 있습니다. 우주. 여기에서 개별 단어는 고유 한 배열 요소로 저장되며 "${array[@]}" 확장은 각 요소를 별도의 셸 단어로 확장합니다.

# define the array mycmd=(ls -l "/tmp/test/my dir") # run the command "${mycmd[@]}" 

구문은 약간 끔찍하지만 배열을 사용하면 명령 줄을 하나씩 만들 수도 있습니다. 예 :

mycmd=(ls) # initial command if [ "$want_detail" = 1 ]; then mycmd+=(-l) # optional flag fi mycmd+=("$targetdir") # the filename "${mycmd[@]}" 

또는 명령 줄의 일부를 일정하게 유지하고 배열을 사용하여 옵션 또는 파일 이름과 같은 일부만 채 웁니다.

options=(-x -v) files=(file1 "file name with whitespace") target=/somedir transmutate "${options[@]}" "${files[@]}" "$target" 

배열의 단점은 “표준 기능이 아니므로 일반 POSIX 셸 (예 : dash, 기본값 Debian / Ubuntu의 /bin/sh)는 지원하지 않습니다 (아래 참조). 그러나 Bash, ksh 및 zsh는 수행하므로 시스템에 배열을 지원하는 일부 셸이있을 가능성이 높습니다.

이름이 지정된 배열을 지원하지 않는 셸에서도 위치 매개 변수 (의사 배열 "$@")는 명령의 인수를 포함합니다.

다음은 이전 섹션의 코드 비트와 동일한 작업을 수행하는 이식 가능한 스크립트 비트 여야합니다. 배열은

, 위치 매개 변수 목록입니다."$@"설정은set및 큰 따옴표로 수행됩니다."$@"주변이 중요합니다 (이로 인해 목록의 요소가 개별적으로 인용 됨).

먼저, "$@"에 인수가있는 명령을 저장하고 실행하기 만하면됩니다.

set -- ls -l "/tmp/test/my dir" "$@" 

명령 줄 옵션의 일부 조건부 설정 :

set -- ls if [ "$want_detail" = 1 ]; then set -- "$@" -l fi set -- "$@" "$targetdir" "$@" 

옵션 및 피연산자에 "$@" 만 사용 :

set -- -x -v set -- "$@" file1 "file name with whitespace" set -- "$@" /somedir transmutate "$@" 

(물론 "$@"는 일반적으로 스크립트 자체에 대한 인수로 채워져 있으므로 사용자는 ” "$@" 용도를 변경하기 전에 어딘가에 저장해야합니다.)


eval에주의하세요. !

eval는 추가 수준의 인용 및 확장 처리를 도입하므로 사용자 입력에주의해야합니다. 예를 들어, 이는 사용자가 작은 따옴표를 입력하지 않음 :

read -r filename cmd="ls -l "$filename"" eval "$cmd"; 

하지만 "$(uname)".txt 입력을 제공하면 스크립트가 만족스러워집니다. 명령 대체를 실행합니다.

배열이있는 버전은 th에 영향을받지 않습니다. 단어가 전체 시간 동안 별도로 유지되기 때문에 “filename의 내용에 대해 인용 또는 기타 처리가 없습니다.

read -r filename cmd=(ls -ld -- "$filename") "${cmd[@]}" 

참조

댓글

  • cmd="ls -l $(printf "%q" "$filename")"를 수행하여 평가 인용을 처리 할 수 있습니다. 예쁘지는 않지만 사용자가 eval를 사용하는 데 실패했다면 도움이됩니다. ‘는 ssh foohost "ls -l $(printf "%q" "$filename")"와 같은 유사한 항목을 통해 명령을 전송하거나 다음 질문의 내용으로 매우 유용합니다. ssh foohost "$cmd".
  • 직접적으로 관련이 없지만 디렉토리를 하드 코딩 했습니까? 이 경우 별칭을 살펴볼 수 있습니다. 예 : $ alias abc='ls -l "/tmp/test/my dir"'

답변

(사소하지 않은) 명령을 실행하는 것은 eval입니다. 그런 다음 명령 줄에서 수행하는 것처럼 명령을 작성할 수 있으며 방금 입력 한 것처럼 정확히 실행됩니다. 하지만 모든 것을 인용해야합니다.

간단한 경우 :

abc="ls -l "/tmp/test/my dir"" eval "$abc" 

그렇지 않은 경우 :

# command: awk "! a[$0]++ { print "foo: " $0; }" inputfile abc="awk "\""! a[$0]++ { print "foo: " $0; }"\"" inputfile" eval "$abc" 

댓글

  • 안녕하세요 @HaukeLaging, \ ‘ ‘?
  • @jumping_monkey ' 인용 문자열 내에 '를 포함 할 수 없습니다. . 따라서 (1) 따옴표를 끝내거나 중단하고, (2) 다음 '를 이스케이프하여 문자 그대로 가져와야합니다. (3) 리터럴 따옴표를 입력하고 (4) 중단 된 경우 따옴표로 묶인 문자열을 다시 시작합니다. (1)'(2)\(3)'(4)'
  • 안녕하세요 @HaukeLaging, 흥미 롭습니다. abc='awk \'! a[$0]++ { print "foo: " $0; }\' inputfile' ?
  • @jumping_monkey 이것이 작동하지 않는 이유를 방금 설명 드린 것은 말할 것도 없습니다. 코드를 게시하기 전에 테스트하는 것이 ‘ 이치에 맞지 않습니까?
  • li>

답변

두 번째 따옴표는 명령을 중단합니다.

실행할 때 :

abc="ls -l "/home/wattana/Desktop"" $abc 

오류가 발생했습니다.

하지만 실행할 때

abc="ls -l /home/wattana/Desktop" $abc 

전혀 오류가 없습니다.

당시 (저에게는)이 문제를 해결할 수있는 방법이 없지만 디렉토리 이름에 공백이 없으면 오류를 피할 수 있습니다.

이 답변 은 eval 명령을 사용하여이 문제를 해결할 수 있지만 작동하지 않는다고 말했습니다. : (

댓글

  • 예, ‘가 필요하지 않는 한 작동합니다. 공백이 포함 된 파일 이름 (또는 glob 문자가 포함 된 파일).

Answer

"" 인 경우 ``를 사용해야합니다.

abc=`ls -l /tmp/test/my\ dir/` 

업데이트 개선 방법 :

abc=$(ls -l /tmp/test/my\ dir/) 

설명

  • 명령의 결과를 변수에 저장합니다. OP는 (이상하게도) 명령 자체를 변수에 저장하려고합니다. 아, 그리고 정말 백틱 대신 $( command... )를 사용해야합니다.
  • 설명과 팁에 감사드립니다!

답변

python3 원 라이너는 어떻습니까?

bash-4.4# pwd /home bash-4.4# CUSTOM="ls -altr" bash-4.4# python3 -c "import subprocess; subprocess.call(\"$CUSTOM\", shell=True)" total 8 drwxr-xr-x 2 root root 4096 Mar 4 2019 . drwxr-xr-x 1 root root 4096 Nov 27 16:42 .. 

의견

  • 문제는 파이썬이 아니라 BASH에 관한 것입니다.

답변

변수 :

$ history -s $abc 

그리고 UpArrow 또는 Ctrl-p를 눌러 명령 줄로 가져옵니다. 다른 방법과는 달리 필요한 경우 실행하기 전에 편집 할 수 있습니다.

이 명령은 변수 내용을 Bash 기록에 새 항목으로 추가하며 UpArrow.

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다