N개의 파일이 포함된 디렉터리를 생각해 보세요.
파일을 알파벳순으로 나열하고 다음과 같이 목록을 저장할 수 있습니다.
ls > list
그런 다음 동일한 디렉토리에 n개의 하위 디렉토리를 만들고 싶습니다.
mkdir direc{1..n}
list
이제 처음 m개 또는 처음 5개(1-5)개의 파일을 에서 로 이동 하고 direc1
다음 5개 파일(예: 6-10) 을 로 이동하고 싶습니다 direc2
.
이것은 당신에게 사소한 일처럼 보일 수도 있지만, 나는 현재 그것을 할 수 없습니다. 도와주세요.
답변1
list=(*) # an array containing all the current file and subdir names
nd=5 # number of new directories to create
((nf = (${#list[@]} / nd) + 1)) # number of files to move to each dir
# add 1 to deal with shell's integer arithmetic
# brace expansion doesn't work with variables due to order of expansions
echo mkdir $(printf "direc%d " $(seq $nd))
# move the files in chunks
# this is an exercise in arithmetic to get the right indices into the array
for (( i=1; i<=nd; i++ )); do
echo mv "${list[@]:(i-1)*nf:nf}" "direc$i"
done
테스트 후 "echo" 명령을 모두 제거하십시오.
또는 각 디렉터리에 고정된 수의 파일을 포함하려면 다음이 더 간단합니다.
list=(*)
nf=10
for ((d=1, i=0; i < ${#list[@]}; d++, i+=nf)); do
echo mkdir "direc$d"
echo mv "${list[@]:i:nf}" "direc$d"
done
답변2
#!/bin/sh
d=1 # index of the directory
f=0 # number of files already copied into direc$d
for x in *; do # iterate over the files in the current directory
# Create the target directory if this is the first file to be copied there
if [ $f -eq 0 ]; then mkdir "direc$d"; fi
# Move the file
mv -- "$x" "direc$d"
f=$((f+1))
# If we've moved 5 files, go on to the next directory
if [ $f -eq 5 ]; then
f=0 d=$((d+1))
fi
done
유용한 참고자료:
- 산술 확장
$((…))
- 조건식
[ … ]
- 공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?왜 인용
--
하고mv -- "$x" …
답변3
d=0; set ./somedirname #init dir counter; name
for f in ./* #glob current dir
do [ -f "$f" ] && set "$f" "$@" && #if "$f" is file and...
[ "$d" -lt "$((d+=$#>5))" ] || continue #d<(d+($#>5)) or continue
mkdir "$6$d" && mv "$@$d" || ! break #mk tgtdir and mv 5 files or
shift 5 #break with error
done
위의 명령은 문자열을 헤드와 테일에 연결하는 쉘의 arg 배열 기능을 활용합니다. 예를 들어, 함수를 작성한다면:
fn(){ printf '<%s>\n' "42$@"; }
...그리고 다음과 같이 부르세요.
fn show me
...다음과 같이 인쇄됩니다.
<42show>
<me>
...첫 번째 또는 마지막 요소에 추가하거나 추가할 수 있기 때문입니다.(각기)arg 배열에서 사전/추가 문자열 주위에 따옴표를 넣으세요.
arg 배열은 카운터 역할도 하기 때문에 여기서 이중 임무를 수행합니다. $#
쉘 인수는 항상 지금까지 쌓인 요소 수를 정확히 알려줍니다.
하지만...단계별 방법은 다음과 같습니다.
d=0; set ./somedirname
$d
새 디렉토리가 생성될 때마다 var는 1씩 증가합니다. 여기서는 0으로 초기화됩니다../somedirname
당신이 좋아하는 방식이에요. 하지만 접두사는 중요합니다./
. 모든 작업을 현재 디렉터리로 루트화할 뿐만 아니라 원하는 모든 종류의 이름을 지정할 수도 있습니다.(새 줄을 사용하거나 하이픈으로 시작하고 싶다면 안전하게 그렇게 할 수 있지만 아마도 바람직하지 않을 것입니다). argname은 항상./
no 명령으로 시작하므로 명령줄에서 옵션으로 잘못 해석될 수 없습니다.
for f in ./*
- 이것은 루프를 시작합니다(그렇다면)
*
현재 디렉터리에서 일치합니다. 마찬가지로, 각 일치 항목에는 접두사가 붙습니다./
.
- 이것은 루프를 시작합니다(그렇다면)
[ -f "$f" ]
- 각 반복에 대한 일치 항목이 확실히 일반 파일인지 확인하세요.(또는 링크)그리고...
set "$f" "$@"
- 일치하는 항목을 쉘 배열에 서로 쌓습니다. 이렇게 하면
./somedirname
항상 배열의 끝에 있습니다.
- 일치하는 항목을 쉘 배열에 서로 쌓습니다. 이렇게 하면
[ "$d" -lt "$((d+=$#>5))" ]
$d
배열에 5개 이상의 요소가 있는 경우 1을 추가하고"$@"
증분 결과를 테스트합니다.
|| continue
- 두 명령 중 하나가
[ -f "$f" ]
set ...
[ "$d" -lt...
true를 반환하지 않으면 루프는 다음 반복으로 계속되고 루프의 나머지 부분을 완료하려고 시도하지 않습니다. 이것은 효율적이다그리고안전한.
- 두 명령 중 하나가
mkdir "$6$d"
- 왜냐하면 이 절은 우리가 지금이고 값이 방금 1만큼 증가한 경우에만 이 지점에 도달할 수 있음을 보장하기 때문입니다
continue
. 따라서 일치하여 이동된 5개 파일의 첫 번째 세트에 대해 다섯 번째 파일에 대해 and라는 디렉터리가 생성됩니다. 중요한 것은 이 명령은$# > 5
./somedirname
$6
$d
./somedirname1
./somedirname5
실패하다대상 경로 이름을 가진 경로 이름이 이미 존재하는 경우. 즉, 이 명령은오직동일한 이름의 디렉터리가 없으면 성공합니다.
- 왜냐하면 이 절은 우리가 지금이고 값이 방금 1만큼 증가한 경우에만 이 지점에 도달할 수 있음을 보장하기 때문입니다
mv "$@$d"
$d
그러면 배열이 확장되어 마지막 요소(대상 디렉터리 이름)의 끝에 값이 추가됩니다 . 따라서 다음과 같이 확장됩니다.
mv ./file5 ./file4 ./file3 ./file2 ./file1 ./somedirname1
- ...정확히 당신이 원하는 일이군요.
|| ! break
어떤 이유로든 처음 두 명령 중 하나가 성공적으로 완료되지 않으면
for
루프가break
중지됩니다. 전송된 반환 값!
의 부울 역수break
(일반적으로 0)이므로break
1이 반환됩니다. 이렇게 하면 이전 명령 중 하나에서 오류가 발생하면 루프가 false를 반환합니다. 이것이 중요합니다.for
루프는while/until
루프와는 달리 테스트하기 위한 것이 아니라 반복하기 위한 것입니다. 이 두 명령의 반환을 명시적으로 테스트하지 않으면 쉘이 오류로 인해 반드시 중지되는 것은 아니며set -e
상위 쉘이 완전히 종료될 수도 있습니다. 대신, 이는 의미 있는 반환을 보장하고 문제가 발생하면 루프가 계속 반복되지 않습니다.언뜻보기에 이것은 여기서 멈출 유일한 대답인 것 같습니다. 예를 들어
mkdir ./somedirname
true가 반환되지 않으면 다른 모든 대답은 루프를 계속합니다.(오류가 반복되거나 더 나쁘게는 현재 디렉터리의 파일을 기존 디렉터리로 이동하여 거기에 있는 동일한 이름의 다른 파일을 덮어쓸 수도 있습니다.). 루프에 임의의 파일 이름을 사용하면 다음을 수행해야 합니다.언제나두 소스 파일이 모두 존재하는지 테스트그리고목표가 존재하기 때문이다.
shift 5
- 이는
shift
쉘 인수 배열의 처음 5개 요소를 제거합니다. 이를 다시 넣고 다음 반복을 위해 배열 상태를 재설정합니다./somedirname
.$1
- 이는
답변4
이 awk 프로그램을 사용하면 쉘 명령을 생성할 수 있으며 의심스러운 경우 해당 명령이 올바른지 미리 확인할 수 있습니다.
awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' list
출력 파이프가 괜찮다면 전체 명령을 sh
.
ls | awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' | sh
n=5 설정을 변경하면 5 이외의 값을 정의할 수 있습니다.
대상 디렉토리를 동적으로 생성하려는 경우 변형은 다음과 같습니다.
ls | awk -v n=5 '
NR%n==1 { ++c ; dir="direc"c ; print "mkdir "dir }
{ printf "mv \"%s\" %s\n", $0, dir }
' | sh