rsync; include-from, files-from. 그리고 Here Document/String, Process Substitution.

간단하게 끝날 거라 생각했던 스크립트였는데, 괜한 우물(?)을 판 꼴이 되어, 어찌됐든 물이 나올 때까진 자리를 벗어날 수 없게 돼 버렸다.

그 이면에는 rsync 의 선택사항인 include-from 과 files-from 이 있다.


아 젠장.

자세하게 원인을 파악하다가는 정신건강에 크나큰 문제가 생길게 염려되어(?), 알아낸 사항만 정리하도록 한다.
먼저, include-from 과 files-from 은 어떻게 다른지 부터.

  • files-from : 실제 경로에 파일이 존재하는지( the exact list of files) 검사. 따라서 목록은 완전한 파일명이어야 한다.
  • include-from : 복사하고자 하는 파일(src) 중, 그런 파일 패턴을 갖고 있는지 여부 검사. 목록은 자체 형식을 갖춰야만 한다.

뭔 말이냐 하면.. 선택한 목록이 다음과 같다고 하자.

mp3
aac
flac

허나, 저 목록 파일을 --files-from 에 넘긴다면, 오류가 발생한다. (물론, 파일명 자체가 ‘mp3’ 라면 선택되겠지만, abc.mp3 는 선택받지 못한다.) 와일드카드가 작동하는지는 아직 시험해보지 않았다.

또, –files-from 은 rsync src 의 경로와도 연관이 되어 있다. 이건 좀 복잡해져서 자세히 쓰기가 귀찮은데..
예를 들어, 다음 파일들이 있다고 가정한다.

/test/foo/bar1.mp3
/test/foo/bar2.flac
/test/foo/bar3.mp3
/test/foo/bar4.aac

선택 목록은 이렇다. (selected.txt 로 가정)

/test/foo/bar2.flac
/test/foo/bar3.mp3

그리고 이런 명령을 내렸다.

$ rsync --files-from=selected.txt /test /home/userabc/cctv-dest 
rsync: link_stat "/test/test/foo/bar2.flac" failed: No such file or directory (2)
rsync: link_stat "/test/test/foo/bar3.mp3" failed: No such file or directory (2)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1207) [sender=3.1.3]

파일을 찾지 못했는데, 오류를 보면 경로가 이상하게 돼 있음을 볼 수 있다.

rsync: link_stat "/test/test/foo/bar2.flac" failed: No such file or directory (2)

파일은 /test/foo/bar2.flac 인데, /test/test/foo/bar2.flac 을 찾았고, (당연히) 이게 없다고 한다.
왜 이 지랄인걸까??

답은, rsync 에서 소스 디렉토리를 /test 로 줬기 때문이다.
이에 관해 man page 에 이렇게 나와있다.

“The filenames that are read from the FILE are all relative to the source dir — any leading slashes are
removed and no “..” references are allowed to go higher than the source dir.”

따라서 저 명령이 유효하려면, 소스 디렉토리를 루트(/)로 바꿔줘야 한다.
이렇기 때문에, files-from 을 쓰기가 좀 애매하고 복잡해진다. 논리로만 보자면, files-from 을 쓰면 굳이 원본 경로를 적을 필요는 없는데, rsync 원 문법(src to dest)이 우선하고, files-from 은 그야말로 선택사항이므로, 그에 따라 이런 요상한 현상이 생겨버린 듯 하다.


그렇다면, 이 명령 그대로 include-from 을 쓰면 아무 이상없이 잘 작동할까?? 라는 순진무구한 생각을 해봤지만..
이게 아니라는 사실을 깨닫기까지는.. 약 2시간 정도와, ‘참을 수 없는 짜증의 소름끼침’이 동반되었다.

왜 안돼…????
형식을 제대로 쓰지 않았기 때문이지.
files-from 은 그냥 검색을 원하는 문구만 넣어주면 된다.

하지만!
include-from 은 + 나 – 등을 넣는 등, 적절한 형식을 지켜야만 한다. (이걸 몰랐으니..)
이 얘기는 다른 곳에서 다시. (할 수 있을까?)


주저리 주저리는 여기까지.
이제부터 본론. (이걸 쓰기 위해 얼마나 달려왔나..)

하고 싶은 건 이거다.

원하는 파일을 스크립트 내에서 find 명령으로 생성했고, 이걸 그냥 변수에만 저장을 했다. 즉, 외부에 파일로 만들지 않았다는 뜻.
이걸 --files-from (또는 --include-from) 에서 쓰려면 어떻게 해야 하나?

왜 그런지는 모르겠지만, 여기선 Process Substitution 만 가능하고, Here String 은 되지 않는다.
이렇게만 쓸 수 있다는 뜻!

rsync -avu --files-from=<(echo "$SRC_FILES") "/" "/home/userabc/test-dest"

$SRC_FILES 에는 find 로 선택한 파일목록이 들어있다는 가정.

이렇게 하면 오류가 발생한다.

rsync -avu --files-from=<<<"$SRC_FILES" "/" "/home/userabc/test-dest"

헌데, Process Substitution, 즉 <(command) 는 Bash 에서만 작동한다고 하며, cron 작업 시에 문제가 발생할 소지도 있다는 주장도 있다. 연결한 글에서 Simon Richter 님이 그런 의견을 주셨다.
또한, 직접 해보니 sudo 로 해도 문제가 발생한다.

따라서, 아래처럼 stdin 을 사용하는 방법이 더 나은 선택이다.
files-from=- 를 쓰면 stdin 을 받아들일 수 있다고 man page 에 나와있다.

cat <<< "$SRC_FILES" | rsync -avu --files-from=- "/" "/home/userabc/test-dest"

마지막으로, find 에 exec 를 써서 rsync 를 불러올 수도 있다.

find /path -file -mtime +3 -exec rsync {} destination \;

어떤게 가장 좋을 지는…
files-from 보다는 include-from 의 패턴 쪽이 좀 더 효과가 있어보이는데, 공부가 필요하다는 점이 큰 문제…!

Author: 아무도안

안녕하세요. 글 남겨주셔서 고맙습니다.