디렉토리에서 가장 큰 파일 5개를 내 파일로 복사하려고 합니다 pwd
. cp specific/directory$(ls -S specific/directory | head -n) ./
첫 번째 파일을 복사한 다음 계속 cannot stat
생성하면 목록의 나머지 파일에 대해 오류가 발생합니다.
파이프라인이 첫 번째 항목에서는 작동하지만 나머지 항목에서는 실패하는 이유는 무엇입니까?
답변1
내 모든 솔루션은파일만, 요청 시, 그리고모든 유형의 파일을 처리할 수 있습니다.
(특수문자도 마찬가지)
사용하고 싶다면ls -S
올바른 방법으로 수행하십시오.
ls --zero -S | head -z -n5 | xargs -r0 cp -t ./other/dir --
최근에 요청했습니다 GNU
coreutils
.
coreutils 9.1-1
여기.
또 다른 방법은 use bash
및 최근 GNU
find
입니다.
findutils 4.9.0-4
여기.
기반으로여기:
shopt -s nullglob
cd specific/directory/ || exit
print0 () {
[ "$#" -eq 0 ] || printf '%s\0' "$@"
}
readarray -td '' files < <(
print0 * |
find -files0-from - -maxdepth 0 -type f -printf '%b\t%p\0' |
sort -rzn |
cut -zf2 -
)
cp -av -- "${files[@]:0:5}" "$OLDPWD"/
${files[@]:0:5}
처음 5개 요소로 확장문서0보다 크거나 같은 키를 가진 배열.
이전 도구의 경우 Perl
모든 셸을 통해
perl -e 'rename($_, "./other/dir/$_") for ((sort { -s $b <=> -s $a } <*>))[0..4]'
답변2
다음을 사용하면 zsh
출력 구문 분석 및 정렬과 관련된 모든 함정을 피할 수 있습니다 ls
.
cp -n -- specific/directory/*(.DOL[1,5]) ./
또는 GNU를 사용하십시오 cp
( -t
옵션의 경우):
cp -n -t ./ -- specific/directory/*(.DOL[1,5])
어디글로벌 예선예
.
일반 파일만 일치합니다(디렉토리, 심볼릭 링크, fifo, 소켓은 일치하지 않음).D
dotglob 옵션 전환 - 숨겨진 파일을 제외하려면 이 옵션을 무시하세요.OL[1,5]
파일 길이(크기)별로 결과를 정렬하고 상위 5개를 선택합니다.
이 -n
옵션은 cp
이름 충돌 시 기존 파일이 손상되는 것을 방지합니다.
답변3
다른 답변 통합:
TL;박사:bash
POSIX 셸에 대한 가능한 솔루션은 아래를 참조하세요.
파이프라인이 첫 번째 항목에서는 작동하지만 나머지 항목에서는 실패하는 이유는 무엇입니까?
쉘은 명령이 가정하는 것과 다르게 동작하기 때문입니다.
명령 $(ls -S | head)
대체는 실제로 해당 출력으로 대체되며 실제로 코드 조각 오른쪽에 바로 붙여넣어집니다 cp specific/directory
.
- 큰따옴표로 묶지 않았기 때문에(이는 오류임) 명령 대체의 출력은 변수를 기반으로 토큰화됩니다
IFS
. 후자의 기본값은(단일 공백) + <tab> + <newline> 문자입니다. <newline>은 명령이
ls -S | head
각 파일 이름을 구분하는 데 사용하는 것이므로 각 이름은 명령을 따르는 별도의 독립 경로가 됩니다cp
. 이 경우 큰따옴표 명령 대체는 도움이 되지 않습니다. - 쉘은 또한
specific/directory/
이름의 모든 부분을 복사하지 않습니다(이것은 prop 확장의 작업이지만 이 경우 올바르게 가져오는 것은 까다로울 수 있습니다). 따라서 첫 번째 개별 이름만이 디렉토리 접두어를 얻습니다. , 따라서 를 통해 액세스할 수 있습니다cp
. 반면 다른 4개의 이름은 현재 디렉토리에 있을 것으로 예상되었지만 분명히 그렇지 않았습니다(그렇다고 하더라도cp
실제로 대상 디렉토리에 있는 파일과 동일한 파일이라는 불만이 있을 것입니다)../
)
"작동"할 수 있습니까? 원칙적으로 그렇습니다. 그러나 n개의 파일 중 하나에 변수에 지정된 문자 중 하나가 포함되면 충돌이 발생하기 때문에 취약합니다 . 전체 제어권이 없으면 IFS
더욱 악화됩니다 ( 1 아래 설명 참조 ).eval
specific/directory
bash
POSIX 쉘에 가능한 솔루션
GNU coreutils v9.0 이상을 사용할 때 사용할 수 있는 다른 답변에 언급된 솔루션 외에도 쉘ls --zero
변형을 제공하는 GNU coreutils v8.25(2016년 경) 이상을 사용하여 안전하게 수행할 수도 있습니다. 이를 위해 우리는ls
--quoting-style
필요를 사용하세요 eval
. 이것이 ls
실제로 작동하도록 설계된 이 옵션의 이점을 누릴 수 있는 유일한 방법이기 때문입니다.그리고 eval
.
평소와 eval
마찬가지로, 특별한 주의를 기울여 처리해야 합니다. 여기서는 ls
명령 에만 사용 하고 ls
문서화된 동작을 기반으로 셸의 파일 이름을 올바르게 인용하는 데 의존합니다. 추가적인 주의를 기울이려면, /bin/ls
우연히 존재하거나 의도적으로 이름이 지정되었음을 아는 내보낸 악성 함수(또는 별칭)를 사용하는 위험을 감수하는 대신 필요한 옵션을 제공하는 실행 파일에 대한 명시적인 전체 경로를 호출할 수 있습니다.ls
--quoting-style
ls
$PATH
ls
따라서 bash
:
(
set -o pipefail \
&& o="$(/bin/ls -S --quoting-style=shell-escape-always | head -n 5)" \
&& eval "set -- $o" \
&& (("$#")) && cp -n -- "${@/#/specific/directory/}" .
)
로 변경할 수 있습니다 head -n 5
.
위의 코드 조각에서는 추가 안전 및 오류 검사를 추가했지만 실제로는 모든 것을 기본 명령으로 줄일 수 있습니다.만약에누구세요확실히 긍정적이다귀하의 ls
버전과 관련하여 실패하거나 잘못된 문자를 출력할 실제 이유는 없습니다.
(cd specific/directory && \
eval "cp -n -- $(ls -S --quoting-style=shell-escape-always | head -n 5)"' "$OLDPWD"')
위의 POSIX 쉘용 솔루션과 동등한 솔루션도 안전하게 작동합니다1. 하지만 명령에서 제공하는 전체 파일 목록을 메모리에 로드해야 하기 때문에 완전히 이상적이지는 않습니다 ls
. 이러한 목록이 셸에 도달하기 전에 필터링할 수 없기 때문에 소스 디렉터리에는 사용 가능한 메모리를 채울 만큼 충분한 파일이 포함되어서는 안 됩니다. 그렇지 않으면 다음 명령을 실행하기 전에 셸이 종료됩니다 cp
.
(
set -- && cd specific/directory \
&& o="$(/bin/ls -rSxw 0 --quoting-style=shell-always)" && eval "set -- $o" \
&& [ "$#" -gt 0 ] && n="$(($# - 5))" && shift "$(($n > 0 ? $n : 0))" \
&& cp -n -- "$@" "$OLDPWD"
)
여기에서 비트를 변경하여 상위 n개 파일의 수를 변경할 수 있습니다 $(($# - 5))
.
이 bash
버전과 마찬가지로 필요한 전제 조건을 다시 파악하는 한 이 버전도 약간 간소화될 수 있습니다. 간소화된 버전 외에도 bash
최소한 n개의 파일이 필요합니다.실제로그렇지 않으면 shift
명령이 실패하여 셸이 조기에 종료됩니다(예를 들어 에 파일이 5개 미만인 경우 specific/directory
이 간단한 버전에서는 해당 파일을 복사하지 않습니다).
(
set -- && cd specific/directory \
&& eval "set -- $(ls -rSxw 0 --quoting-style=shell-always)" \
&& shift "$(($# - 5))" && cp -n -- "$@" "$OLDPWD"
)
1
노트:단순화와 설명을 위해 위의 솔루션은아니요파일이 실제로 존재하는지 확인정기적인파일만(즉, 디렉토리나 심볼릭 링크, 소켓, 명명된 fifo, 장치 파일은 제외) 따라서 소스 디렉터리가하다이러한 "파일"이 처음으로 가장 큰 n개 파일 중에 존재하는 경우가 발생합니다(유효 개수가 0바이트임에도 불구하고). 위의 해결 방법은~ 할 것이다최종 명령 cp
에 이러한 이름을 포함합니다 . 이는 특히 기호 링크 및 디렉토리와 관련이 있습니다.언제나내용에 따라 개수가 0보다 크므로 순위가 더 높을 수 있습니다. ls -S
한 수준은 버려진 파일을 대체합니다. 여기 내 솔루션은 이미 bash
POSIX 셸의 기능을 확장하므로 이러한 상황을 제대로 처리하려면 다른 답변을 참조하세요 .
답변4
편집: 새로운 답변, 더 완전한 작업:
디렉터리 이름이 첫 번째 결과에만 추가되므로 원본은 실패하므로 현재 디렉터리에 존재하지 않는 나머지 결과는 "해당 파일 없음" 오류가 발생합니다.
바람직하지 않은 방법 은 inode 유형을 나타내는 후행 문자를 포함하는 옵션을 find
이용하는 것입니다 . 다음은 목록에서 디렉토리를 제거하는 불완전한 답변입니다 . 더 완전한 답변은 제외되어야 하는 다른 inode 유형을 제거합니다. 이 명령은 실행 파일에 추가된 .-F
ls
grep
sed
*
-F
source="<some directory name>"
destination='.'
someCount=5 # e.g.
while IFS=\ read -r; do
cp "${source}/${REPLY}" "${destination}"
done <<<"$(ls "${source}" -Ft | grep -v '/$' | head -5 | sed 's/\*$//')"
원래 답변:
가장 큰 파일이 1, 2, 3, 4라고 가정해 보겠습니다. 질문의 명령은 다음과 같습니다.
cp specific/directory/one two three four .
2, 3, 4가 없기 때문에 명령이 실패합니다. 비슷하다
source=specific/directory
set -f # disable globbing
IFS='
' # split on newlines only
for file in $(ls -S $source); do
cp "${source}/${file}" .
done
그렇게 할 것이다.
경고: 파일 이름에 개행 문자가 있으면 중단됩니다(또는 ls
터미널에 인쇄하지 않아도 파일 이름이 손상됨).