사용: rsync 버전 3.1.0 프로토콜 버전 31(기반: Ubuntu 14.04.3)
rsync를 사용하는 백업 Bash 스크립트 중 하나에서 rsync 옵션을 다음과 같은 변수에 넣었습니다.
# Set rsync command options.
rsync_options="-e ssh -axhPv"
if [ "$deletion_type" = "DELETE_ON_DESTINATION" ]; then
rsync_options="$rsync_options --delete"
fi
if [ "$run_type" = "DRY_RUN" ]; then
rsync_options="$rsync_options --dry-run"
fi
# Run the backup.
rsync "$rsync_options" --log-file="$log_file" --exclude-from="$exclude_file" \
"$source_dir" "$destination_dir"
오류가 나타납니다.
unknown option -- h
usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
[-D [bind_address:]port] [-E log_file] [-e escape_char]
[-F configfile] [-I pkcs11] [-i identity_file]
[-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec]
[-O ctl_cmd] [-o option] [-p port]
[-Q cipher | cipher-auth | mac | kex | key]
[-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port]
[-w local_tun[:remote_tun]] [user@]hostname [command]
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: unexplained error (code 255) at io.c(226) [sender=3.1.0]
"$rsync_options"
본능적으로 Bash 스크립트에서 따옴표를 제거하고 다시 시도했습니다.
rsync $rsync_options --log-file="$log_file" --exclude-from="$exclude_file" \
"$source_dir" "$destination_dir"
...이 문제가 해결되었고 훌륭하게 작동합니다.
Bash 스크립트의 변수에 명령 옵션을 넣는 것은 여러 번 해본 일이지만, 특정 경우는 생각나지 않지만 가끔 변수 이름 주위의 큰따옴표를 제거해야 했던 기억이 납니다(이번 외에는) , 확실히).
내 질문은 따옴표가 문제를 일으키는 이유는 무엇입니까? rsync 또는 Bash와 관련된 특정 문제로 인해 발생합니까? 이 문제를 이해하고 싶습니다.
감사해요.
답변1
옵션 세트는 다음을 사용하여 시각화할 수 있습니다 printf
.
$ printf '<%s> ' "-e ssh -axhPv" arg2 arg3 ; echo
<-e ssh -axhPv> <arg2> <arg3>
따옴표를 제거하면 다음과 같은 결과가 나타납니다.
$ printf '<%s> ' -e ssh -axhPv arg2 arg3 ; echo
<-e> <ssh> <-axhPv> <arg2> <arg3>
즉, 인수는 공백으로 구분되어 명령에 별도의 요소로 제공됩니다.
대량으로
그러나 일반적인 해결책은 따옴표를 제거하는 것이 아닙니다. 이는 각 매개변수를 IFS 분할 및 경로 이름 확장에 노출시킵니다.
올바른 해결책은 값 배열을 사용하는 것입니다.
$ a=(-e ssh -axhPv arg2 arg3)
$ printf '<%s> ' "${a[@]}"; echo
<-e> <ssh> <-axhPv> <arg2> <arg3>
이렇게 하면 공백이 있는 옵션도 추가할 수 있습니다.
$ a+=("arg with spaces" "one more")
$ printf '<%s> ' "${a[@]}"; echo
<-e> <ssh> <-axhPv> <arg2> <arg3> <arg with spaces> <one more>
따라서 명령에 인수를 제공하는 방법을 제어할 수 있습니다.
스크립트는 다음을 다시 작성합니다.
#!/bin/bash
# Set rsync command options.
rsync_options=( "-e" "ssh" "-axhPv" )
if [ "$deletion_type" = "DELETE_ON_DESTINATION" ]; then
rsync_options+=( "--delete" )
fi
if [ "$run_type" = "DRY_RUN" ]; then
rsync_options+=("--dry-run")
fi
log_file="/var/tmp/log/script.sh"
exclude_file="/var/tmp/log/excludethis"
source_dir=/a
destination_dir=/b
# Run the backup.
rsync_options+=('--log-file="'"$log_file"'"')
rsync_options+=('--exclude-from="'"$exclude_file"'"')
rsync_options+=("$source_dir")
rsync_options+=("$destination_dir")
### Remove the printf to actually run the command:
printf '<%s> ' rsync "${rsync_options[@]}"
참고: 배열에 넣을 수 없는 한 가지는 리디렉션입니다.
답변2
rsync_options를 rsync 명령에 참조하면 이러한 모든 옵션을 별도의 옵션이 아닌 하나의 인수로 rsync에 전달합니다. 따라서 rsync는 "-axhPv" 플래그를 사용하여 ssh를 실행하려고 시도합니다.
시연 시간:
function showme () {
echo First: $1
echo Second: $2
echo Third: $3
}
$ showme "-e ssh -axhPv" two three
First: -e ssh -axhPv
Second: two
Third: three
$ showme -e ssh -axhPv two three
First: -e
Second: ssh
Third: -axhPv
답변3
-e 매개변수 자체를 따옴표로 묶어야 한다고 생각합니다.
rsync -axhPv -e 'ssh -p 2222' user@host:/source_dir/ /dest_dir/
따라서 다음을 수행해야 할 수도 있습니다.
rsync_options="-e 'ssh' -axhPv"
큰따옴표 내에서 작은따옴표를 이스케이프 처리해야 할 수도 있고 필요하지 않을 수도 있습니다.