다음 줄로 프로그램을 작성했는지 말해보십시오.
int main(int argc, char** argv)
이제의 내용을 확인하여 어떤 명령 행 인수가 전달되는지 알고 argv
있습니다.
프로그램이 인수 사이에 얼마나 많은 공백을 감지 할 수 있습니까? 내가 bash에 이것을 입력 할 때와 같이 :
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
환경은 최신 Linux (예 : Ubuntu 16.04)이지만 POSIX 호환 시스템에 답을 적용해야한다고 생각합니다.
답변
“인수 사이의 공백”에 대해 말하는 것은 의미가 없습니다. 그것은 쉘 개념입니다.
쉘의 임무는 전체 입력 행을 인수로 배열하여 명령을 시작하는 것입니다. 따옴표 붙은 문자열 구문 분석, 변수 확장, 파일 와일드 카드 및 물결표 식 등이 포함될 수 있습니다. 명령은 exec
문자열 벡터를 허용하는 표준 시스템 호출 로 시작 됩니다.
문자열 벡터를 생성하는 다른 방법이 있습니다. 많은 프로그램이 미리 정해진 명령 호출로 자체 하위 프로세스를 포크하고 실행합니다.이 경우 “명령 줄”과 같은 것은 없습니다. 마찬가지로, 그래픽 (데스크톱) 쉘은 사용자가 파일 아이콘을 끌어서 명령 위젯에 놓을 때 프로세스를 시작할 수 있습니다.
호출 된 명령에 관한 한, 쉘 또는 다른 상위 / 전구 자 프로세스에서 진행되는 작업은 개인용이며 숨겨져 main()
있습니다. 표준 C에서 허용 할 수있는 문자열 배열 만 볼 수 있습니다.
답변
일반적으로 아닙니다. 명령 행 구문 분석은 구문 분석되지 않은 행을 호출 된 프로그램에서 사용할 수 없게하는 쉘에 의해 수행됩니다. 실제로 프로그램은 문자열을 구문 분석하는 것이 아니라 인수 배열을 프로그래밍 방식으로 구성하여 argv를 만든 다른 프로그램에서 실행될 수 있습니다.
답변
공백이 인수의 일부 가 아닌 한 불가능합니다 .
명령은 배열 (프로그램 언어에 따라 한 형태 또는 다른 형태)에서 개별 인수에 액세스하고 실제 명령 행은 히스토리 파일 (히스토리 파일이있는 쉘의 대화식 프롬프트에 입력 된 경우)에 저장 될 수 있습니다. 어떤 형태로든 명령에 전달되지 않았습니다.
유닉스의 모든 명령은 결국 exec()
함수 계열 중 하나에 의해 실행됩니다 . 이들은 명령 이름과 인수 목록 또는 배열을 사용합니다. 그들 중 어느 것도 쉘 프롬프트에서 입력 된 명령 행을 취하지 않습니다. 이 system()
함수는 수행하지만 문자열 인수는 나중에 execve()
명령 행 문자열이 아닌 인수 배열을 사용하여 나중에 실행됩니다 .
답변
일반적으로 다른 여러 답변과 같이 불가능합니다.
그러나 유닉스 쉘 입니다 일반 프로그램 (그들은 명령 행을 해석하고있다 로빙 즉, 그것을 확장 하기 전에 명령을 fork
& execve
그것을 위해). 쉘 조작 에 대한bash
이 설명을 참조하십시오 . 당신은 당신 자신의 쉘을 작성할 수도 있고 (또는 GNU bash 와 같은 기존의 자유 소프트웨어 쉘을 패치 할 수도 있습니다) 그것을 쉘 (또는 로그인 쉘, passwd (5) & shells (5) 참조 )로 사용할 수도 있습니다.
예를 들어, 당신은 할 수있는 당신의 자신의 쉘 프로그램이 일부 환경 변수에 전체 명령 줄을 넣어 (상상 MY_COMMAND_LINE
예를 들어) – 또는 다른 종류의 사용 프로세스 간 통신을 쉘에서 자식 프로세스 레에 명령 줄을 전송합니다.
왜 그렇게하고 싶은지 이해하지 못하지만 그러한 방식으로 동작하는 쉘을 코딩 할 수도 있습니다 (그러나 그렇게하지 않는 것이 좋습니다).
BTW, 프로그램은 쉘 이 아닌 일부 프로그램에 의해 시작될 수 있습니다 (그러나 fork (2) 다음 execve (2) 또는 execve
현재 프로세스에서 프로그램을 시작하는 프로그램). 이 경우 명령 줄이 전혀 없으며 명령없이 프로그램을 시작할 수 있습니다 …
쉘이 설치되지 않은 일부 (전문화 된) Linux 시스템이있을 수 있습니다. 이것은 이상하고 이례적이지만 가능합니다. 그런 다음 전문 작성해야 초기화 로 다른 프로그램을 시작 프로그램을 필요로 – 어떤 쉘을 사용하지 않고 있지만 수행하여 fork
및 execve
시스템 호출.
또한 읽기 운영 체제 : 세 가지 쉬운 조각 하고 그 잊지 마세요 execve
거의 항상이다 시스템 호출 (리눅스, 그들이에 나와있는 콜 (2) 참조 또한 소개 (2) )을 다시 초기화 가상 주소 공간 (및 일부 다른 프로세스 ) .
답변
쉘에게 항상 어떤 쉘 코드가 실행을하는지 알려주도록 쉘에 지시 할 수 있습니다. 예를 들어 with를 사용하여 후크를 사용하여 환경 변수 zsh
에 해당 정보를 전달 하면 (예를 들어 프로그램에서 사용 ) :$SHELL_CODE
preexec()
printenv
getenv("SHELL_CODE")
$ preexec() export SHELL_CODE=$1
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv SHELL_CODE
printenv CODE
$ $(echo printenv SHELL_CODE)
$(echo printenv SHELL_CODE)
$ for i in SHELL_CODE; do printenv "$i"; done
for i in SHELL_CODE; do printenv "$i"; done
$ printenv SHELL_CODE; : other command
printenv SHELL_CODE; : other command
$ f() printenv SHELL_CODE
$ f
f
모든 printenv
것은 다음과 같이 실행 됩니다.
execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"],
["PATH=...", ..., "SHELL_CODE=..."]);
해당 인수로 printenv
실행되는 zsh 코드를 검색 할 수 printenv
있습니다. 그 정보로 당신이하고 싶은 것은 분명하지 않습니다.
와 bash
기능에 가장 가까운, zsh
S는 ‘ preexec()
그것을 사용하는 것입니다 $BASH_COMMAND
A의 DEBUG
함정,하지만 노트 bash
(잘 일부) (특히 refactors에 구분 기호로 사용되는 공백의 일부 등)와 그의 모든에 적용한다는 점에서 다시 작성 일정 수준의 수행 명령 프롬프트에서 입력 한 전체 명령 줄이 아니라 실행합니다 ( functrace
옵션 참조 ).
$ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv $(echo 'SHELL_CODE')
printenv $(echo 'SHELL_CODE')
$ for i in SHELL_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printf '%s\n' "$(printenv "SHELL_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printenv "SHELL_CODE"
$ print${-+env } $(echo 'SHELL_CODE')
print${-+env } $(echo 'SHELL_CODE')
쉘 언어 구문에서 분리 문자 인 일부 공백이 1로 압축 된 방법과 전체 명령 행이 항상 명령에 전달되지 않는 방법을보십시오. 따라서 귀하의 경우에는 유용하지 않을 것입니다.
다음과 같이 모든 명령에 민감한 정보가 유출 될 수 있으므로 이런 종류의 작업을 수행하지 않는 것이 좋습니다.
echo very_secret | wc -c | untrustedcmd
wc
그리고 그 비밀을 모두에게 누설 할 것 untrustedcmd
입니다.
물론, 쉘 이외의 다른 언어에 대해서도 이런 종류의 작업을 수행 할 수 있습니다. 예를 들어 C에서는 명령을 실행하는 C 코드를 환경에 내보내는 매크로를 사용할 수 있습니다.
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)
int main(int argc, char *argv[])
{
if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (!fork()) WRAP(0 + execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
wait(NULL);
return 0;
}
예:
$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])
Bash 사례와 같이 C 전 처리기에서 일부 공간이 어떻게 압축되었는지 확인하십시오. 모든 언어는 아니지만 대부분의 언어에서 구분 기호에 사용되는 공간의 양에는 차이가 없으므로 컴파일러 / 통역사가 여기에서 자유 로워지는 것은 놀라운 일이 아닙니다.
답변
나는 다른 답변에서 누락 된 것을 추가 할 것입니다.
아니
다른 답변보기
아마도
프로그램에서 수행 할 수있는 작업은 없지만 프로그램을 실행할 때 셸에서 수행 할 수있는 작업이 있습니다.
따옴표를 사용해야합니다. 그래서 대신
./myprog aaa bbb
이 중 하나를 수행해야합니다
./myprog " aaa bbb"
./myprog ' aaa bbb'
이것은 공백과 함께 하나의 인수를 프로그램에 전달합니다. 둘 사이에는 차이가 있으며, 두 번째는 리터럴이며, 표시된 그대로 정확하게 문자열입니다 (를 '
입력해야합니다 \'
). 첫 번째 문자는 일부 문자를 해석하지만 여러 인수로 나뉩니다. 자세한 내용은 쉘 인용을 참조하십시오. 따라서 셸을 다시 작성할 필요가 없습니다. 셸 디자이너는 이미 생각했습니다. 그러나 이제는 하나의 주장이므로 프로그램 내에서 더 많은 전달을해야합니다.
옵션 2
stdin을 통해 데이터를 전달하십시오. 이것은 많은 양의 데이터를 명령으로 가져 오는 일반적인 방법입니다. 예 :
./myprog << EOF
aaa bbb
EOF
또는
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(이탤릭체는 프로그램의 출력입니다)