현재 사용자의 홈 디렉터리를 임의로 선택한 디렉터리에 복사하고 rsync를 사용하여 동일한 디렉터리(존재하는 경우)에서 가장 최근에 생성된 복사본에 연결하는 스크립트를 작성 중입니다.
명령은 다음과 같이 빌드되고 실행됩니다.
...
[ -z $LAST_SNAPSHOT ] && LINK_DEST="" || LINK_DEST="--link-dest \"$BACKUP_ROOT/$LAST_SNAPSHOT\""
...
/usr/bin/rsync $OPTS $EXCLUDES $LINK_DEST "$MASTER/" "$NEW_SNAPSHOT"
rsync 스크립트를 실행하면 다음 오류가 발생합니다.
--link-dest arg does not exist: "/home/backuptest/dest/backuptest/20180619_134044"
이는 디렉토리가 실제로 존재하지 않지만 존재하는 경우 예상됩니다.
변수를 생성하는 코드에서 따옴표를 제거하면 $LINK_DEST
다음 코드가 생성됩니다.
[ -z $LAST_SNAPSHOT ] && LINK_DEST="" || LINK_DEST="--link-dest $BACKUP_ROOT/$LAST_SNAPSHOT"
그러면 rsync가 불평하지 않습니다. 경로에 공백이 없도록 하고 싶으므로 큰따옴표가 필요합니다.
오류가 무엇인지 이해할 수 없습니다. rsync를 호출하는 방식인가요, 아니면 쉘(bash)을 오해하고 있는 건가요?
답변1
이스케이프된 큰따옴표는 파일 이름의 리터럴 문자로 처리됩니다. 를 사용하고 있으므로 bash
배열을 사용하여 이를 처리할 수 있습니다.
linkDest=()
[[ -n "$LAST_SNAPSHOT" ]] && linkDest+=('--link-dest' "$BACKUP_ROOT/$LAST_SNAPSHOT")
...
rsync $OPTS $EXCLUDES "${linkDest[@]}" "$MASTER/" "$NEW_SNAPSHOT"
"${linkDest[@]}"
참조가 비어 있으면 참조가 완전히 사라집니다. 그렇지 않으면 해당 값의 참조 목록으로 확장됩니다. $OPTS
과 가 목록 이었다면 $EXCLUDES
동일한 메커니즘을 사용할 것입니다.
답변2
문제는 파일 이름의 일부로 큰따옴표가 있다는 것입니다.
옵션을 저장하려면 배열을 사용하십시오 rsync
.
셸 에서 /bin/sh
(셸에서도 작동 가능 bash
):
# options that are always set
set -- --archive --verbose
# add --link-dest=DIR if needed
if [ -n "$LAST_SNAPSHOT" ]; then
set -- "$@" --link-dest="$BACKUP_ROOT/$LAST_SNAPSHOT"
fi
# add some --excludes
set -- "$@" --exclude='*.ext' --exclude='dir/***'
# call rsync
rsync "$@" "$MASTER/" "$NEW_SNAPSHOT"
bash
이는 위치 인수 배열인 POSIX 셸에서 사용할 수 있는 유일한 배열(확장, 유사 등이 아닌 경우)을 활용합니다 . 이 배열의 항목을 설정하고( 를 사용하여 set
) 필요에 따라 추가함으로써 rsync
인용된 인수(또는 모든 명령)가 올바르게 처리되도록 할 수 있습니다. 호출에서 배열의 항목이 큰따옴표로 개별적으로 묶여 있는지 rsync
확인 "$@"
하여 쉘이 파일 이름을 토큰화하고 생성하는 것을 방지합니다.
동등한 것이지만 bash
배열을 사용하는 경우:
# options that are always set
opts=( --archive --verbose )
# add --link-dest=DIR if needed
if [ -n "$LAST_SNAPSHOT" ]; then
opts+=( --link-dest="$BACKUP_ROOT/$LAST_SNAPSHOT" )
fi
# add some --excludes
opts+=( --exclude='*.ext' --exclude='dir/***' )
# call rsync
rsync "${opts[@]}" "$MASTER/" "$NEW_SNAPSHOT"
답변3
차이점은 이스케이프 따옴표가 \"
이를 문자열 문자로 변환한다는 것입니다.
따라서 오류가 정확할 수 있습니다.
"/홈/백업테스트/대상/백업테스트/20180619_134044"
이름에서 따옴표가 포함된 부분은 다음과 같습니다.
/홈/백업 테스트/대상/백업 테스트/20180619_134044
이것이 당신이 원하는 것입니다. 이 문제를 해결하기 위해 권장되는 방법은 ${} 변수를 사용하는 것입니다. 예를 들면 다음과 같습니다.
$ myvar=var $ echo "\"$myvar\"" "var"
$ echo "${myvar}" var
따라서 "--link-dest "${BACKUP_ROOT}"/"${LAST_SNAPSHOT}""과 같은 명령을 사용하면 더 가까워질 것입니다.
이 콘텐츠에 대한 추가 정보: https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables 그리고 http://wiki.bash-hackers.org/syntax/pe#simple_usage