헤드의 출력을 사용하여 공백이 있는 파일 복사

헤드의 출력을 사용하여 공백이 있는 파일 복사

폴더에 이름에 공백이 포함된 파일이 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

이것은 양식의 stdout 용어로 인쇄됩니다.

Timestamp Filename

여기서 타임스탬프는 Epoch 및 파일 이름이 인용된 파일 이름 버전이므로 수정 날짜( -r의 옵션을 사용하여 얻은)(초)입니다.date쉘 입력으로 재사용할 수 있음help printf( 및 형식 지정자 참조 %q) 그런 다음 이러한 줄은 를 사용하여 (숫자, 역순) 정렬되며 sort처음 10개만 유지됩니다.

그런 다음 이것은 while루프에 공급됩니다. 타임스탬프는 file=${file# *}할당과 함께 제거됩니다(이것은 첫 번째 공백까지의 모든 것을 제거합니다). 그런 다음 명백히 위험한 줄은 eval "file=$file"에 의해 소개된 이스케이프 문자를 제거 printf %q하고 마침내 파일을 안전하게 복사할 수 있습니다.

아마도 최선의 접근 방식이나 구현은 아닐 수 있지만 가능한 파일 이름과 관련하여 100% 안전이 보장되며 작업이 완료됩니다. 그러나 이는 일반 파일, 디렉토리 등을 모두 동일하게 처리합니다. 일반 파일로 제한하려면 해당 줄 [[ -f $f ]] || continue바로 뒤에 추가하십시오 for 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

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


또 다른 접근 방식: and 인수 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"
}

완전히 이식 가능하며 반환할 문자에 관계없이 안전하게 쉘로 묶인 파일 이름 배열을 인쇄합니다. 귀하의 경우 폰을 삭제하기만 하면 되므로 다음과 같이 한 번만 실행하면 됩니다.

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

관련 정보