내 폴더에 이름에 공백이 포함된 파일이 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 ]] || 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
이전 방법과 마찬가지로 일반 파일, 디렉터리 등을 동일하게 처리하고 숨겨진 파일을 건너뜁니다.
또 다른 접근 방식: 및 매개변수가 ls
있는 경우 다음을 사용할 수 있습니다.i
q
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