head의 출력을 사용하여 공백이 있는 파일을 복사합니다.

head의 출력을 사용하여 공백이 있는 파일을 복사합니다.

내 폴더에 이름에 공백이 포함된 파일이 11개 있는데 최신 10개 파일을 복사하고 싶습니다. 예전에는 ls -t | head -n 10최신 10개 파일만 가져올 수 있었습니다. cp 문에 표현식을 사용하려고 하면 이름에 공백이 있어서 파일을 찾을 수 없다는 오류가 발생합니다.

예를 들면 cp: cannot stat ‘10’: No such file or directory다음과 같습니다 .10 abc.pdf

CP 문:cp $(ls -t | head -n 10) ~/...

어떻게 작동하게 할 수 있나요?

답변1

Bash를 사용하고 있고 100% 안전한 방법을 원한다면(파일 이름에 주의해야 하는 어려운 방법을 배웠으므로 여러분도 원한다고 가정합니다) 다음을 수행할 수 있습니다.

shopt -s nullglob
while IFS= read -r file; do
    file=${file#* }
    eval "file=$file"
    cp "$file" Destination
done < <(
    for f in *; do
        printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
    done | sort -rn | head -n10
)

그 중 몇 가지 부분을 살펴보겠습니다.

for f in *; do
    printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
done

이것은 양식의 표준 출력 용어로 인쇄됩니다.

Timestamp Filename

여기서 타임스탬프는 에포크( -r옵션을 통해 얻은 date) 이후의 수정 날짜(초)이고 파일 이름은 인용된 파일 이름 버전입니다.쉘 입력으로 재사용 가능(형식 지정자를 참조하세요 help printf) %q. 그런 다음 행을 숫자순, 역순으로 정렬 sort하고 처음 10개 행만 유지합니다.

while그런 다음 루프 에 공급하십시오 . 타임스탬프는 할당 file=${file# *}(첫 번째 공백 이전의 모든 항목 제거)으로 제거되고, 명백히 위험한 줄에는 eval "file=$file"이스케이프 문자가 제거되고 printf %q마지막으로 파일을 안전하게 복사할 수 있습니다.

아마도 최선의 접근 방식이나 구현은 아닐 수 있지만 가능한 모든 파일 이름에 대해 100% 안전이 보장되며 작업이 완료됩니다. 그러나 이는 일반 파일, 디렉토리 등을 동일하게 처리합니다. 일반 파일로 제한하려면 [[ -f $f ]] || continuefor f in *; do줄 뒤에 추가하세요. 또한 숨겨진 파일은 고려하지 않습니다. 파일을 숨기려면(물론 그렇지 .않음 ..) shopt -s dotglob.


또 다른 100% Bash 솔루션은 Bash를 직접 사용하여 파일을 정렬하는 것입니다. 빠른 정렬을 사용하는 방법은 다음과 같습니다.

quicksort() {
    # quicksorts the filenames passed as positional parameters
    # wrt modification time, newest first
    # return array is quicksort_ret
    if (($#==0)); then
        quicksort_ret=()
        return
    fi
    local pivot=$1 oldest=() newest=() f
    shift
    for f; do
        if [[ $f -nt $pivot ]]; then
            newest+=( "$f" )
        else
            oldest+=( "$f" )
        fi
    done
    quicksort "${oldest[@]}"
    oldest=( "${quicksort_ret[@]}" )
    quicksort "${newest[@]}"
    quicksort_ret+=( "$pivot" "${oldest[@]}" )
}

그런 다음 이를 정렬하고 상위 10개를 유지한 다음 대상에 복사합니다.

$ shopt -s nullglob
$ quicksort *
$ cp -- "${quicksort_ret[@]:0:10}" Destination

이전 방법과 마찬가지로 일반 파일, 디렉터리 등을 동일하게 처리하고 숨겨진 파일을 건너뜁니다.


또 다른 접근 방식: 및 매개변수가 ls있는 경우 다음을 사용할 수 있습니다.iq

ls -tiq | head -n10 | cut -d ' ' -f1 | xargs -I X find -inum X -exec cp {} Destination \; -quit

그러면 파일의 inode가 표시되고 find해당 inode가 참조하는 파일에서 명령을 실행할 수 있습니다.

다시 말하지만, 이는 일반 파일뿐만 아니라 디렉토리 등도 처리합니다. ls출력 형식 에 너무 많이 의존하기 때문에 나는 이것을 별로 좋아하지 않습니다 ...

답변2

디렉터리의 모든 파일 그룹에 대해 항상 가장 오래된 파일을 무시합니다.

less_oldest() {
    while [ -e "$1" ] ; do
        for f do [ "$f" -ot "$1" ] && break
        done && { set -- "$@" "$1"
            shift ; continue
        } ; shift ; break
    done
    printf '///%s///\n' "$@" |
        sed "s|'"'|&"&"&|g;'"s|///|'|g"
}

이는 완전히 이식 가능하며 반환할 수 있는 문자에 관계없이 쉘 안전 파일 이름 배열을 인쇄합니다. 귀하의 경우에는 dro pone만 필요하므로 다음과 같이 한 번만 실행하면 됩니다.

{   printf 'cp -t ~"/..."'
    less_oldest "${dir_of_11_files}/"*
} | sh

관련 정보