제목에서 알 수 있듯이 나는 bash에서 다음을 수행하는 프로그램을 작성하고 싶습니다.
디렉토리의 모든 파일을 반복합니다(각 디렉토리에는 100개의 파일이 있습니다).
1에서 100 사이의 할당되지 않은 난수를 할당합니다.
파일 이름을 X(할당된 난수) 파일 이름 형식으로 다른 파일(results.txt)에 저장합니다.
파일 이름을 임의의 숫자로 변경
파일을 반복하는 방법을 알고 있지만 나머지는 내 능력을 약간 벗어납니다. 이 문제에 대한 귀하의 도움에 매우 감사하겠습니다 :)
답변1
이 연습의 가장 큰 어려움은 100개 파일 각각에 대해 고유한 난수를 생성하는 것입니다. 이 문제에 대한 해결책은 필요한 숫자(1~100)를 생성한 다음 숫자와 파일 이름을 연결하기 전에 숫자를 섞는 것(또는 파일 이름 목록을 섞는 것)입니다. 마지막으로 하고 싶은 일은 100면 주사위를 굴려 이미 해당 숫자를 굴렸는지 확인하고, 그렇다면 이전에 굴리지 않은 숫자가 나올 때까지 주사위를 다시 굴리는 것입니다. 파일 수가 많은 경우 프로세스가 필요할 수 있습니다.아주 오랜 시간. (흥미롭게도 우리는 이것을 몇 년 전에 인터뷰 질문으로 사용했습니다.)
파일 이름에 개행 문자가 포함되어 있지 않다고 가정합니다.
paste <( printf '%d\n' {1..100} ) <( printf '%s\n' dir/* | sort -R ) >result.txt
그러면 유틸리티를 사용하여 탭으로 구분된 두 개의 열이 생성됩니다 paste
. 첫 번째 열에는 1부터 100까지의 정수가 순서대로 포함됩니다. 두 번째 열에는 디렉터리에 있는 100개 파일의 이름 dir
(디렉터리 이름 포함)이 포함됩니다. 이름 목록은 무작위 순서로 정렬됩니다( -R
옵션 sort
은 표준이 아니지만 일반적으로 사용 가능).
정수를 섞는 대신 파일 이름을 정렬할 수도 있습니다.
paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' dir/* ) >result.txt
파일 이름을 변경하려면 result.txt
파일을 읽으십시오.
while IFS= read -r stuff; do
number=${stuff%%$'\t'*} # the thing before the first tab
pathname=${stuff#*$'\t'} # the thing after the first tab
mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done <result.txt
현재 디렉터리의 모든 하위 디렉터리에 대해 이 명령을 실행합니다(이 명령을 실행하기 전에 두 번 생각하고 항상 중요한 데이터의 백업을 유지하십시오).
for dirpath in */; do
paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' "$dirpath"/* )
done >result.txt
while IFS= read -r stuff; do
number=${stuff%%$'\t'*}
pathname=${stuff#*$'\t'}
mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done <result.txt
혹은 좀 더 간략하게 말하자면,
for dirpath in */; do
paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' "$dirpath"/* )
done |
tee result.txt |
while IFS= read -r stuff; do
number=${stuff%%$'\t'*}
pathname=${stuff#*$'\t'}
mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done
답변2
이 답변은진주 rename
유틸리티( prename
또는 file-rename
). 아니요rename
util-linux
명령줄 옵션과 기능이 완전히 다른 from 또는 다른 rename
명령 과 혼동됩니다 .
Perl 이름 바꾸기 유틸리티의 장점 중 하나는 상대적으로 간단한 sed와 같은 파일 이름 변환(예: rename 's/foo/bar/' *
)을 수행할 수 있을 뿐만 아니라 다음을 사용할 수도 있다는 것입니다.어느Perl에 구현된 알고리즘을 사용하여 파일 이름을 바꿉니다. 각 파일 이름은 Perl의 특수 변수에 저장되며 $_
이름이 변경됩니다.만약에 그리고 만약에$_이(가) 변경되었습니다.
다음 한 줄의 이름 바꾸기 코드가 수행하는 작업은 다음과 같습니다.
$ rename -n '
BEGIN{
open(RESULTS,">","results.txt");
};
our $rnd = 0;
our @used;
# find a random number from 1..100 that hasnt been used yet.
until (($rnd > 0) && (!defined($used[$rnd]))) {
$rnd=int(rand(100)+1);
};
$used[$rnd]=1;
print RESULTS "$rnd\t$_\n";
$_ = $rnd' *
시험 실행을 위한 옵션으로 만들어서 -n
그것이 무엇을 하는지 보여줍니다.rename
회의허락하신다면 그렇게 하세요.
실제로 파일 이름을 바꾸도록 제거 -n
(또는 자세한 출력으로 대체 )하십시오.-v
그런데 rename
이름을 바꿀 파일 이름 목록은 명령줄 인수로 제공하거나 STDIN에서 또는 둘 다에서 제공할 수 있습니다. 또한 주목할 만한 점은 -0
NUL로 구분된 입력 옵션(예: from find ... -print0
)을 지원한다는 것입니다.
참고: 원래 파일 이름에 개행 문자가 포함된 경우 해당 results.txt
파일을 사용하여 이름을 바꾸는 데 안정적으로 사용할 수 없습니다. \n
확실하지 않은 경우 개행 문자 대신 NUL을 사용하여 results.txt의 각 레코드를 구분하십시오. 즉, 이름 바꾸기 스크립트의 마지막에서 두 번째 줄을 다음으로 바꿉니다.
print RESULTS "$rnd\t$_\0";
참고로 반전:
$ rename -n '
our %files;
BEGIN{
open(RESULTS,"<","results.txt");
# local $/ = "\0"; # uncomment for NUL-separated input
while(<RESULTS>){
chomp;
my ($n,$f) = split /\t/,$_,2;
$files{$n} = $f;
};
close(RESULTS);
};
if (defined($files{$_})) { $_ = $files{$_} };
' [0-9]*
이 ,2
줄은 split /\t/,$_,2
입력 레코드에 여러 탭이 있더라도 분할을 최대 2개의 필드로 제한하므로 파일 이름의 탭으로 인해 스크립트가 중단되지 않습니다.
간단한 반전으로 sed -e 's/^/mv /' results.txt | sh
인해 공백, 탭 등이 포함된 파일 이름이나 셸 메타 문자가 손상됩니다.
참고: 방금 Kusalananda의 답변을 읽었으며 이 답변을 지적할 가치가 있다고 생각했습니다.하다d100을 반복해서 굴려서 해당 난수를 사용했는지 확인하세요.
부분적으로는 단지 100개의 난수가 성능에 거의 또는 전혀 영향을 미치지 않고 부분적으로는 perl에서 성능 문제가 훨씬 작기 때문에...bash는 느리고 bash의 루프는 특히 느립니다. 하지만 대부분 그의 답변을 읽을 때까지 나는 그것에 대해 생각하지 않았기 때문입니다 :)
이론적으로 이 루프는 무한할 수도 있고(난수 시퀀스는 정말 좋지 않음) 시간이 오래 걸릴 수도 있습니다. 실제로는 두 가지 결과가 모두 발생할 가능성이 매우 낮습니다. 단, 사용되지 않은 숫자를 생성하면 루프를 반복할 때마다 시간이 더 오래 걸릴 수 있습니다(그러나 이는 사람이 알아차릴 가능성조차 없습니다).
List::Util
예를 들어 모듈의 기능을 사용하여 K의 답변과 같은 임의의 배열을 미리 생성하는 것이 가능합니다 shuffle()
.
$ rename -n '
use List::Util qw(shuffle);
our $i;
our @random;
BEGIN{
@random = shuffle 1..100;
$i=0;
open(RESULTS,">","results.txt");
};
print RESULTS "$random[$i]\t$_\n";
$_ = $random[$i++]' *
답변3
그리고 zsh
:
n=0; for f in dir/*(noe['REPLY=$RANDOM']); do
mv -i -- $f $f:h/$((++n)) &&
print -r -- $f was renamed to $n
done > result.txt
glob oe
한정자는 제공된 표현식(여기서는 0에서 32767 사이의 임의의 숫자를 반환함)의 평가를 기반으로 glob이 확장되는 순서를 정의합니다. 이는 파일처럼 섞인 순서를 효과적으로 제공합니다.
그런 다음 파일 이름을 순차적으로 증가하는 숫자로 바꾸므로 디렉터리에 파일 수에 관계없이 작동합니다.