디렉토리가 존재하는 경우에만 디렉토리 목록을 압축하는 방법

디렉토리가 존재하는 경우에만 디렉토리 목록을 압축하는 방법

보관하고 싶은 디렉토리 목록이 있습니다. 그러나 때로는 그것들이 모두 존재하지 않는 경우도 있습니다.

나는 디렉토리 목록을 제공하여 아카이브를 생성할 수 있기를 원하며 이는 기존 디렉토리만 아카이브하고 누락된 디렉토리는 무시합니다. (그러나 디렉토리가 존재하지 않으면 여전히 실패합니다)

이는 지속적인 통합을 실행 중이고 특정 프로세스가 나중에 사용하기 위해 아카이브에 보관하고 싶은 특정 아티팩트를 생성하기 때문에 나에게 효과적입니다. 생성할 수 있는 가능한 모든 경로를 알고 있지만 어떤 경로가 무엇인지 항상 확신할 수는 없습니다. 만들어진.

가능한 경로는 다음과 같다고 가정합니다.here_is_a_dir here_is_another_one yet_another_dir

나는 일반적으로 다음 명령을 사용하여 아카이브를 만듭니다.

tar -czf archive.tgz here_is_a_dir here_is_another_one yet_another_dir

물론 디렉터리가 누락된 경우에는 실패합니다.

이상적으로는 간단한 명령이어야 하며 완료하는 데 스크립트가 필요하지 않습니다. (구체적으로는 제 환경에서만 가능해서 남들처럼 화려한 쉘은 sh쓸 수 없지만 bash이건 제 환경에 따라 다르고 앞으로 바뀔 수도 있으니 다른 쉘을 사용하는 답변도 아주 좋을 것 같습니다.)

답변1

사용된 출력은 ls일반적으로 현명하지 못하고 안전하지 않습니다. 공백과 개행은 물론 기타 쉘 메타 문자도 파일이나 디렉토리 이름에서 유효한 문자라는 점을 기억하십시오. 이 문제는 많은 경우에 해결될 수 있지만 그렇게 하려면 일반적으로 find작업에 적합한 도구(예:)를 사용하는 것보다 더 많은 노력이 필요합니다.

그러니 find대신에 그것을 사용하세요. 예를 들어:

find . -maxdepth 1 -type d \( -name here_is_a_dir -o -name here_is_another_one \
  -o -name yet_another_dir \) -exec tar cfz archive.tgz {} +

-type d이는 현재 디렉터리( )에서 일치하는 디렉터리( )를 찾아 .이를 tar명령에 대한 인수로 사용합니다. to는 OR을 사용 \(하여 \)각 하위 표현식을 결합하는 표현식입니다 -o(기본적으로 find의 조건자는 AND 결합입니다). 즉, "최대 깊이 1 AND 유형 디렉토리 AND (dir1 OR dir2 OR dir3)"라고 읽습니다.

우선 순위를 강제하는 괄호가 없으면 "최대 깊이 1 AND 유형 디렉토리 AND dir1 OR (dir2 OR dir3)"로 해석되며 기존 디렉토리의 전체 목록을 반환하지 않습니다. 대부분의 경우 dir1이 존재하는지 여부에 따라 아무것도 반환하지 않거나 dir3만 반환합니다.

현재 디렉터리 아래의 하위 디렉터리를 찾으려면 이 -maxdepth 1매개변수를 제거하세요.

디렉토리 일치에서 대소문자를 구분하지 않으려면 -iname대신 를 사용하십시오 -name. -nameor 의 인수는 -iname고정 문자열이 아닌 패턴일 수 있습니다. 이는 필수 디렉토리 이름이 매우 유사한 경우에 유용합니다. 예를 들어

find . -maxdepth 1 -type d -iname 'dir[123]' -exec tar cfz archive.tgz {} +

이는 -exec ....파이프 to 와 매우 유사하게 작동 xargs하지만 to 에 내장되어 있습니다 find. 실제로 xargs원한다면 모든 -exec것을 -print0 | xargs -0 -r tar cfz archive.tgz. 예를 들어

find . -maxdepth 1 -type d -iname 'dir[123]' -print0 | \
  xargs -0 -r tar cfz archive.tgz 

(이것은 NUL 문자를 출력 구분 기호로 사용하므로 공백 등이 포함된 디렉토리 이름과 함께 사용하는 것이 안전합니다. -exec-r옵션은 입력이 없으면 xargs에 아무것도 하지 않도록 지시합니다)

답변2

그리고 zsh:

dirs_to_archive=(some/dir /some/other/dir and/more/dirs)
existing_dirs=($^dirs_to_archive(/N))
if (($#existing_dirs)); then
  tar -cf - -- $existing_dirs | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi

POSIX 해당 명령(그러나 POSIX 명령 tar이나 xzPOSIX 명령이 아님)은 다음과 유사합니다.

# The list of dirs in "$@" (the only array in POSIX sh language)
set -- some/dir /some/other/dir and/more/dirs

for dir do
  # remove from the array the elements that are not directories like with
  # zsh's / glob qualifier above
  [ -d "$dir" ] && [ ! -L "$dir" ] && set -- "$@" "$dir"
  shift
done
if [ "$#" -gt 0 ]; then
  tar -cf - -- "$@" | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi

답변3

Bash에 액세스할 수 있는 경우 확장 와일드카드( extglob)를 사용하세요.

$ shopt -s extglob; set -x
$ tar -czf archive.tgz *(here_is_a_dir|here_is_another_one|yet_another_dir)
+ tar -czf archive.tgz
tar: no files or directories specified

다음 중 하나 이상이 존재하는 경우:

$ touch here_is_a_dir yet_another_dir
+ touch here_is_a_dir yet_another_dir
$ tar -czvf archive.tgz *(here_is_a_dir|here_is_another_one|yet_another_dir)
+ tar -czvf archive.tgz here_is_a_dir yet_another_dir
a here_is_a_dir
a yet_another_dir

set -x( 글로브 확장 결과를 확인하기 위해 사용했습니다 .)

답변4

POSIX sh( )에서만 tar.sh사용해야 하는 것 :

#!/bin/sh

first=1
for dir in "$@"; do
        if [ "$first" ]; then  # this is a bit ugly
                set --
                first=
        fi
        if [ -d "$dir" ]; then
                set -- "$@" "$dir"
        fi
done
echo tar -czf archive.tar.gz "$@"    # remove that 'echo'

시험:

$ mkdir here_is_a_dir yet_another_dir
$ ./tar.sh here_is_a_dir here_is_another_one yet_another_dir
tar -czf archive.tar.gz here_is_a_dir yet_another_dir

관련 정보