백틱을 사용하기에 적절한 시기(그리고 잘못된 시기)는 언제입니까?

StackOverflow https://stackoverflow.com/questions/115809

  •  02-07-2019
  •  | 
  •  

문제

많은 초보 프로그래머는 다음과 같은 코드를 작성합니다.

sub copy_file ($$) {
  my $from = shift;
  my $to = shift;

  `cp $from $to`;
}

이것이 나쁜 것입니까? 왜 그렇습니까?백틱을 사용해야 합니까?그렇다면 어떻게?

도움이 되었습니까?

해결책

몇몇 사람들은 이미 다음과 같은 경우에만 백틱을 사용해야 한다고 언급했습니다.

  • 출력을 캡처(또는 억제)해야 합니다.
  • 동일한 작업을 수행하는 내장 기능이나 Perl 모듈이 없거나 해당 모듈이나 내장을 사용하지 않을 충분한 이유가 있습니다.
  • 입력 내용을 위생 처리합니다.
  • 반환 값을 확인합니다.

안타깝게도 반환 값을 확인하는 것과 같은 것들은 제대로 꽤 어려울 수 있습니다.신호 때문에 죽었나요?실행이 완료되었지만 이상한 종료 상태가 반환되었나요?해석을 시도하는 표준 방법 $? 정말 끔찍해요.

나는 다음을 사용하는 것이 좋습니다 IPC::시스템::단순 모듈의 capture() 그리고 system() 백틱이 아닌 기능.그만큼 capture() 함수는 다음을 제외하고 백틱과 동일하게 작동합니다.

  • 명령이 시작되지 않거나, 신호에 의해 종료되거나, 예기치 않은 종료 값을 반환하는 경우 자세한 진단을 제공합니다.
  • 오염된 데이터가 전달되면 자세한 진단을 제공합니다.
  • 이는 허용 가능한 종료 값을 지정하기 위한 쉬운 메커니즘을 제공합니다.
  • 원한다면 쉘 없이 백틱을 호출할 수 있습니다.
  • 단일 인수를 사용하더라도 셸을 피할 수 있는 안정적인 메커니즘을 제공합니다.

Perl의 내장 명령과 달리 명령은 운영 체제와 Perl 버전 전반에 걸쳐 일관되게 작동합니다. system() 이전 버전의 Perl(예: 여러 인수가 있는 5.6.0)에서 여러 인수를 사용하여 호출할 때 오염된 데이터를 확인하지 못할 수 있거나 Windows에서 어쨌든 셸을 호출할 수 있습니다.

예를 들어 다음 코드 조각은 호출 결과를 다음 위치에 저장합니다. perldoc 스칼라로 변환하고 셸을 피하고 페이지를 찾을 수 없으면 예외를 발생시킵니다(perldoc가 1을 반환하므로).

#!/usr/bin/perl -w
use strict;
use IPC::System::Simple qw(capture);

# Make sure we're called with command-line arguments.
@ARGV or die "Usage: $0 arguments\n";

my $documentation = capture('perldoc', @ARGV);

IPC::시스템::단순 순수 Perl이고 5.6.0 이상에서 작동하며 일반적으로 Perl 배포판과 함께 제공되지 않는 종속성이 없습니다.(Windows에서는 Win32에 따라 다릅니다::ActiveState 및 Strawberry Perl과 함께 제공되는 모듈).

부인 성명:나는 다음의 저자이다 IPC::시스템::단순, 그래서 약간의 편견을 보일 수 있습니다.

다른 팁

규칙은 간단합니다.동일한 작업을 수행하는 내장 기능을 찾을 수 있거나 해당 기능을 수행할 CPAN의 강력한 모듈인 경우 백틱을 사용하지 마십시오.백틱은 이식할 수 없는 코드에 의존하는 경우가 많으며 변수를 제거하더라도 여전히 많은 보안 허점에 노출될 수 있습니다.

절대 허용되는 것을 매우 엄격하게 지정하지 않는 한 사용자 데이터에 백틱을 사용하십시오(허용되지 않는 것이 아니라 내용을 놓칠 것입니다)!이것은 매우 매우 위험합니다.

백틱은 명령의 출력을 캡처해야 하는 경우에만 사용해야 합니다.그렇지 않으면 system()을 사용해야 합니다.그리고 물론 해당 작업을 수행하는 Perl 기능이나 CPAN 모듈이 있는 경우 둘 중 하나 대신 이를 사용해야 합니다.

두 경우 모두 다음 두 가지 사항을 적극 권장합니다.

첫 번째, 모든 입력을 삭제합니다. 코드가 신뢰할 수 없는 입력에 노출될 경우 Taint 모드(-T)를 사용하십시오.그렇지 않더라도 공백이나 세 가지 종류의 따옴표와 같은 이상한 문자를 처리(또는 방지)해야 합니다.

두번째, 반환 코드를 확인하세요 명령이 성공했는지 확인합니다.이를 수행하는 방법의 예는 다음과 같습니다.

my $cmd = "./do_something.sh foo bar";
my $output = `$cmd`;

if ($?) {
   die "Error running [$cmd]";
}

(pid 및 종료 코드 외에) stdout을 캡처하는 또 다른 방법은 다음을 사용하는 것입니다. IPC::오픈3 시스템과 백틱의 사용을 모두 무효화할 수도 있습니다.

명령의 출력을 수집하려면 백틱을 사용하세요.

그렇지 않으면 system() 특히 메타 문자나 명령 구문 분석을 처리하기 위해 셸을 호출할 필요가 없는 경우에는 더 나은 선택입니다.system()에 목록을 전달하면 이를 방지할 수 있습니다. 예를 들어 system('cp', 'foo', 'bar') (그러나 아마도 모듈을 사용하는 것이 더 나을 것입니다. 특정한 예 :))

Perl에는 원하는 작업을 수행하는 방법이 항상 여러 가지 있습니다.백틱의 주요 요점은 쉘 명령의 표준 출력을 Perl 변수로 가져오는 것입니다.(예제에서는 cp 명령이 인쇄하는 모든 내용이 호출자에게 반환됩니다.) 예에서 백틱 사용의 단점은 쉘 명령의 반환 값을 확인하지 않는다는 것입니다.cp가 실패할 수 있으며 사용자는 눈치채지 못할 것입니다.특별한 Perl 변수 $?와 함께 이것을 사용할 수 있습니다.쉘 명령을 실행하고 싶을 때 나는 다음을 사용하는 경향이 있습니다. 체계:

system("cp $from $to") == 0
    or die "Unable to copy $from to $to!";

(또한 공백이 포함된 파일 이름에서는 이 작업이 실패한다는 점에 유의하십시오. 그러나 이것이 질문의 요점은 아니라고 생각합니다.)

다음은 백틱이 유용할 수 있는 인위적인 예입니다.

my $user = `whoami`;
chomp $user;
print "Hello, $user!\n";

더 복잡한 경우에는 다음을 사용할 수도 있습니다. 열려 있는 파이프로:

open WHO, "who|"
    or die "who failed";
while(<WHO>) {
    # Do something with each line
}
close WHO;

"perlop" 맨페이지에서:

그렇다고 백티크가 무언가를 끝내는 올바른 방법 일 때 백 티크를 피하기 위해 길을 떠나야한다는 의미는 아닙니다.Perl은 접착제 언어로 만들어졌으며, 함께 접착 된 것 중 하나는 명령입니다.자신이 무엇을하고 있는지 이해하십시오.

당신이 사용하여 보여주는 경우 파일::복사 모듈이 아마도 가장 좋을 것입니다.그러나 귀하의 질문에 대답하기 위해 저는 시스템 명령을 실행해야 할 때마다 일반적으로 의존합니다. IPC::런3.이는 반환 코드, 표준 및 오류 출력 수집과 같은 많은 기능을 제공합니다.

무엇을 하든지 입력을 삭제하고 코드의 반환 값을 확인하는 것 외에도 명시적인 전체 경로를 사용하여 외부 프로그램을 호출해야 합니다.예를 들어말하다

my $user = `/bin/whoami`;

또는

my $result = `/bin/cp $from $to`;

"whoami" 또는 "cp"라고만 말하면 사용자 경로가 변경된 경우 실수로 의도한 것과 다른 명령을 실행할 위험이 있습니다. 이는 악의적인 공격자가 악용할 수 있는 보안 취약점입니다.

귀하의 예는 백틱 대안보다 이식 가능하고 일반적으로 더 효율적인 작업을 수행하는 Perl 내장 기능이 있기 때문에 좋지 않습니다.

Perl 내장(또는 모듈) 대안이 없는 경우에만 사용해야 합니다.이는 백틱 및 system() 호출 모두에 사용됩니다.백틱은 실행된 명령의 출력을 캡처하기 위한 것입니다.

백틱은 출력을 캡처하려는 경우에만 사용되어야 합니다.여기에서 그들을 사용하는 것은 "어리석은 것처럼 보입니다." 그것은 당신이 Perl에 익숙하지 않다는 사실에 당신의 코드를 보는 사람에게 단서를 얻을 것입니다.

출력을 캡처하려면 백틱을 사용하세요.명령을 실행하려면 system을 사용하십시오.얻을 수 있는 이점 중 하나는 반품 상태를 확인할 수 있다는 것입니다.이식성을 위해 가능한 경우 모듈을 사용하십시오.이 경우에는 File::Copy가 적합합니다.

일반적으로 사용하는 것이 가장 좋습니다. 체계 백틱 대신:

  1. 체계 호출자가 명령의 반환 코드를 확인하도록 권장합니다.

  2. 체계 보다 안전하고 유연성을 추가하는 "간접 개체" 표기법을 허용합니다.

  3. 백틱은 문화적으로 쉘 스크립팅과 연결되어 있으며 이는 코드 독자들 사이에서는 일반적이지 않을 수 있습니다.

  4. 백틱은 무거운 명령에 대해 최소한의 구문을 사용합니다.

사용자가 백틱 대신 백틱을 사용하고 싶어하는 이유 중 하나 체계 사용자에게 STDOUT을 숨기는 것입니다.이는 STDOUT 스트림을 리디렉션하여 더 쉽고 유연하게 수행할 수 있습니다.

my $cmd = 'command > /dev/null';
system($cmd) == 0 or die "system $cmd failed: $?"

또한 STDERR을 제거하는 것은 쉽게 수행됩니다.

my $cmd = 'command 2> error_file.txt > /dev/null';

백틱을 사용하는 것이 적합한 상황에서는 백틱을 사용하는 것을 선호합니다. qx{} 무거운 명령이 일어나고 있음을 강조하기 위해.

반면에 다른 방법을 사용하면 정말 도움이 될 수 있습니다.때로는 명령이 STDOUT에 인쇄하는 내용을 확인해야 하는 경우도 있습니다.백틱은 쉘 스크립트에서처럼 사용될 때 작업에 딱 맞는 도구입니다.

Perl은 분리된 성격을 가지고 있습니다.한편으로는 쉘 사용을 대체할 수 있는 훌륭한 스크립트 언어입니다.이러한 일회성 결과 관찰 사용에서는 백틱이 편리합니다.

프로그래밍 언어를 사용할 때는 백틱을 피해야 합니다.이는 오류 점검의 부족이며, 별도의 프로그램 백 티크 실행을 피할 수 있다면 효율성을 얻을 수 있습니다.

위의 내용 외에도 명령의 출력을 사용하지 않는 경우 시스템 기능을 사용해야 합니다.

백틱은 아마추어를 위한 것입니다.방탄 솔루션은 "Safe Pipe Open"("man perlipc" 참조)입니다.다른 프로세스에서 명령을 실행하면 먼저 STDERR, setuid 등을 사용할 수 있습니다.장점:그렇습니다 ~ 아니다 신뢰할 수 없는 open("$cmd $args|")과 달리 @ARGV를 구문 분석하기 위해 쉘에 의존합니다.기본 프로그램의 동작을 변경하지 않고도 STDERR을 리디렉션하고 사용자 권한을 변경할 수 있습니다.이는 역따옴표보다 더 장황하지만 run_cmd($cmd,@args);와 같은 자체 함수로 래핑할 수 있습니다.


sub run_cmd {
  my $cmd = shift @_;
  my @args = @_;

  my $fh; # file handle
  my $pid = open($fh, '-|');
  defined($pid) or die "Could not fork";
  if ($pid == 0) {
    open STDERR, '>/dev/null';
    # setuid() if necessary
    exec ($cmd, @args) or exit 1;
  }
  wait; # may want to time out here?
  if ($? >> 8) { die "Error running $cmd: [$?]"; }
  while (<$fh>) {
    # Have fun with the output of $cmd
  }
  close $fh;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top