2009-04-27 11 views
15

나는 주어진 미디어 디렉토리를 tars하고, 그 결과물을 암호화 한 다음, 백업 미디어가 거대한 파일들을 지원하지 않기 때문에 결과 파일을 다수의 작은 파일들로 나눠주는 아주 간단한 bash 스크립트를 작성하고있다.bash 스크립트의 명령으로서의 변수

저는 bash 스크립팅에 대한 많은 경험이 없습니다. 매개 변수의 공백을 허용하기 위해 변수를 올바르게 인용하는 데 문제가 있다고 생각합니다. 스크립트는 다음과 같습니다

분할 : "foo는/2009-04-27T14-32-04.backup"AA : 해당 파일이나 디렉토리

이 명령을 실행

#! /bin/bash 

# This script tars the given directory, encrypts it, and transfers 
# it to the given directory (likely a USB key). 

if [ $# -ne 2 ] 
then 
    echo "Usage: `basename $0` DIRECTORY BACKUP_DIRECTORY" 
    exit 1 
fi 

DIRECTORY=$1 
BACKUP_DIRECTORY=$2 
BACKUP_FILE="$BACKUP_DIRECTORY/`date +%Y-%m-%dT%H-%M-%S.backup`" 

TAR_CMD="tar cv $DIRECTORY" 
SPLIT_CMD="split -b 1024m - \"$BACKUP_FILE\"" 

ENCRYPT_CMD='openssl des3 -salt' 

echo "$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD" 

$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD 

say "Done backing up" 

실패

내가 $SPLIT_CMD으로 설정 한 $BACKUP_FILE 주위의 따옴표를 제거하여 문제를 해결할 수 있습니다. 그러나 백업 디렉토리 이름에 공백이 있으면 작동하지 않습니다. 또한 복사하여 "echo"명령의 출력을 터미널에 직접 붙여 넣으면 제대로 작동합니다. 배시가 어떻게 벗어나는 지 이해할 수없는 것이 분명 있습니다.

+0

왜 당신은 당신이 직후에 넣을 수있을 때 SPLIT_CMD에 $ BACKUP_FILE을 포함 할 것 $ SPLIT_CMD/t 그는 파이프 라인? –

+0

그럼 내가 할 수는 있지만, 그 시점에서 내 명령을 포함 할 변수가있는 것이별로 중요하지 않으며 아래의 줄리아노의 대답처럼 모든 것을 확장 할 수 있습니다. – wxs

+0

http://mywiki.wooledge.org/BashFAQ/050 – tripleee

답변

35

전체 명령을 변수에 넣지 마십시오. 따옴표로 묶인 인수를 복구하는 데 많은 어려움을 겪을 것입니다. 또한

: 모든 수도 스크립트의 변수 이름을 사용

  1. 마십시오. 손발을 쉽게 쏠 수있는 방법.
  2. 역 인용 부호를 사용하지 말고 대신 $ (...)를 사용하십시오. 더 중첩됩니다. 쉘이 일을 다시 해석합니다 있도록 변수 내부 공간을 인용

#! /bin/bash 

if [ $# -ne 2 ] 
then 
    echo "Usage: $(basename $0) DIRECTORY BACKUP_DIRECTORY" 
    exit 1 
fi 

directory=$1 
backup_directory=$2 
current_date=$(date +%Y-%m-%dT%H-%M-%S) 
backup_file="${backup_directory}/${current_date}.backup" 

tar cv "$directory" | openssl des3 -salt | split -b 1024m - "$backup_file" 
+0

그래, 아마 이런 식으로 할거야. 변수에 명령을 사용하는 것보다 나에게 덜 우아 해 보입니다. 그러나 이것이 bash 스크립팅의 본질이라고 생각합니다. – wxs

+4

@wxs : 변수에 명령을 넣는 것에 대해서는 아무 것도 우아하지 않습니다. 어떤 유형의 유연성도 얻지 못합니다. 그와는 반대로, 단어 분할로 인해 버그가 발생합니다. 당신이 의도 한 것은 명령을 함수에 넣는 것입니다. 함수를 실행합니다. 가변 내용을 절대 실행해서는 안됩니다. 이제까지. – lhunath

+7

이해가 안됩니다 1. 설명해 주시겠습니까? 어떻게/왜 발에서 자신을 쏘는 것이 더 쉬워 집니까? – ata

5

확실치 않지만 명령에 대한 평가를 먼저 실행하는 것이 좋습니다. 이것은 배쉬는 자신의 전체 폭에 변수 $ TAR_CMD를 확장하게됩니다

배쉬는, 행에게로 두 번째를 읽습니다 (echo 명령은 작품을 말할 콘솔에가하는 것처럼) 변수가 확장되었습니다.

eval $TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD 

방금 ​​Google 검색을 수행했는데이 페이지는 필요한 이유를 설명하는 데 알맞은 직업이 될 것 같습니다. http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F

+0

그것도 잘하는 것 같지만, 모든 것이 너무 복잡한 것처럼 느껴집니다. 글쎄, 사람들이 왜 다른 스크립팅 언어를 발명했는지 알 겠어. – wxs

+0

이것은 보안상의 주요 위험 요소입니다. 'eval'에 위험이있는 이유에 대한 설명은 http://mywiki.wooledge.org/BashFAQ/050 - BashFAQ # 50을 참조하십시오. http://mywiki.wooledge.org/BashFAQ/048 –

+0

좋은 catch @CharlesDuffy 다른 사용자가 높은 권한으로 실행하기 위해 스크립트에 대한 액세스 권한을 부여받은 공유 시스템에서이 스크립트를 사용하는 경우 위험합니다. – Eddie

1

제대로 하드입니다. 더 강한 언어에 도달하도록 유도하는 것은 이런 유형의 일입니다. 펄이나 파이썬 또는 루비이든간에 (필자는 perl을 선택하지만, 항상 모든 사람에게 해당되는 것은 아닙니다), 단지 일 뿐이므로 인용을 위해 쉘을 우회 할 수 있습니다.

자유로운 eval을 사용해서는 안되는 것이 아니라, eval은 저에게 eebie-jeebies를줍니다. (사용자 입력과 평가를 원할 때 완전히 새로운 골칫거리가됩니다. 이 경우에는 대신 작성하고 평가 한 내용을 가지고있을 것입니다.) 그리고 디버깅에서 두통을 겪었습니다.-하지만 IO::Pipe, 포크의 비트, 표준 출력과 표준 에러를 재개

@tar_cmd = (qw(tar cv), $directory); 
@encrypt_cmd = (qw(openssl des3 -salt)); 
@split_cmd = (qw(split -b 1024m -), $backup_file); 

어려운 부분은 여기에 파이프를하고있다 : 펄

, 내 예를 들어, 내가 좋아하는 뭔가를 할 수있을 것 , 그리고 나쁘지 않아. 어떤 사람들은 쉘을 적절히 인용하는 것보다 더 나쁘다고 말합니다. 그리고 나는 그들이 어디에서 왔는지 이해합니다. 그러나 나에게 이것은 읽기, 유지 보수 및 쓰기가 더 쉽습니다. 지옥, 누군가는 이것에서 열심히 노력하고 IO :: 파이프 라인 단위를 창조하고 전부를 사소한 것 만들 수 있었다 ;-)

+0

'eval'을 사용하여 어려운 문제가 발생하는 경우에만 문제가됩니다. 그렇게할만한 이유가 없습니다. –

4

변하기 쉬운 명령 및 선택권을 두는 점이있다.

#! /bin/bash 

if [ $# -ne 2 ] 
then 
    echo "Usage: `basename $0` DIRECTORY BACKUP_DIRECTORY" 
    exit 1 
fi 

. standard_tools  

directory=$1 
backup_directory=$2 
current_date=$(date +%Y-%m-%dT%H-%M-%S) 
backup_file="${backup_directory}/${current_date}.backup" 

${tar_create} "${directory}" | ${openssl} | ${split_1024} "$backup_file" 

소스를 다른 파일로 재배치 할 수 있으므로 많은 스크립트에서 동일한 명령과 옵션을 재사용 할 수 있습니다. 이것은 많은 스크립트가 있고 도구를 사용하는 방법을 제어하려고 할 때 매우 편리합니다. 그래서 standard_tools이 포함됩니다 : 디렉토리 이름은 신뢰할 수없는 출처에서 생성 될 수있는 경우

export tar_create="tar cv" 
export openssl="openssl des3 -salt" 
export split_1024="split -b 1024m -" 
+0

'tar_create'가 누락되었지만 그럼에도 불구하고 도움이되었습니다 –

+1

이것은 복잡한 인수에 대해 실제로 문제를 해결하지 않습니다. 'tar_create'가'tar cv --exclude = "* *"'라면 원래와 거의 같은 방식으로 실패했을 것입니다. 그리고'수출'은 여기서는 아무런 도움이되지 않습니다. 이러한 변수는 동일한 쉘에서 사용되므로 하위 프로세스의 환경 공간을 오염시키는 것은 단순히 낭비입니다. –

5

eval이 허용 방법이 아닙니다. 이 문제와 아래에 감동을 일부 적절한 솔루션의 근본 원인에 대한 자세한 내용은 eval는 사용하지 않아야하는 이유에 대한 자세한 내용은 BashFAQ #48BashFAQ #50를 참조하십시오

당신이 시간이 지남에 따라 명령을 구축해야하는 경우 사용 배열 :

tar_cmd=(tar cv "$directory") 
split_cmd=(split -b 1024m - "$backup_file") 
encrypt_cmd=(openssl des3 -salt) 
"${tar_cmd[@]}" | "${encrypt_cmd[@]}" | "${split_cmd[@]}" 

다른 방법으로는, 이것은 단지 하나 개의 중앙 위치에있는 당신의 명령을 정의하는 방법에 대한 경우, 사용 기능 :

tar_cmd() { tar cv "$directory"; } 
split_cmd() { split -b 1024m - "$backup_file"; } 
encrypt_cmd() { openssl des3 -salt; } 
tar_cmd | split_cmd | encrypt_cmd