약 2000 개의 파일이있는 디렉토리가 있습니다. Nbash 스크립트 또는 파이프 명령 목록을 사용하여 임의의 파일 샘플을 선택하려면 어떻게 해야합니까?
ls | shuf -n 5 유닉스 Stackexchange에서 소스
약 2000 개의 파일이있는 디렉토리가 있습니다. Nbash 스크립트 또는 파이프 명령 목록을 사용하여 임의의 파일 샘플을 선택하려면 어떻게 해야합니까?
ls | shuf -n 5 유닉스 Stackexchange에서 소스
답변:
다음은 GNU 정렬의 무작위 옵션을 사용하는 스크립트입니다.
ls |sort -R |tail -$N |while read file; do
# Something involving $file, or you can leave
# off the while to just get the filenames
done
"$file"표시되지 않은 의 사용 만 공백에 민감합니다.
이를 위해 shuf(GNU coreutils 패키지에서) 사용할 수 있습니다 . 파일 이름 목록을 제공하고 무작위 순열에서 첫 번째 줄을 반환하도록 요청하십시오.
ls dirname | shuf -n 1
# probably faster and more flexible:
find dirname -type f | shuf -n 1
# etc..
-n, --head-count=COUNT원하는 줄 수를 반환 하도록 값을 조정하십시오 . 예를 들어 5 개의 임의 파일 이름을 반환하려면 다음을 사용하십시오.
find dirname -type f | shuf -n 5
N임의의 파일 을 선택하고 싶었 으므로 사용하는 1것이 약간 잘못되었습니다.
find dirname -type f -print0 | shuf -zn1
다음은 출력을 구문 분석하지 않고 ls이름에 공백과 재미있는 기호가있는 파일과 관련하여 100 % 안전한 몇 가지 가능성입니다 . 그들 모두는 randf임의의 파일 목록으로 배열 을 채 웁니다 . 이 어레이는 printf '%s\n' "${randf[@]}"필요한 경우 쉽게 인쇄 할 수 있습니다.
이 파일은 동일한 파일을 여러 번 출력 N할 수 있으므로 미리 알려야합니다. 여기서는 N = 42를 선택했습니다.
a=( * )
randf=( "${a[RANDOM%${#a[@]}]"{1..42}"}" )
이 기능은 잘 문서화되어 있지 않습니다.
N을 미리 알지 못했지만 이전 가능성을 정말로 좋아한다면을 사용할 수 있습니다 eval. 그러나 그것은 악한 일이므로 N철저히 확인하지 않고 사용자 입력에서 직접 나오지 않아야합니다!
N=42
a=( * )
eval randf=( \"\${a[RANDOM%\${#a[@]}]\"\{1..$N\}\"}\" )
나는 개인적으로 싫어 eval하고 따라서이 답변!
더 간단한 방법 (루프)을 사용하는 경우에도 마찬가지입니다.
N=42
a=( * )
randf=()
for((i=0;i<N;++i)); do
randf+=( "${a[RANDOM%${#a[@]}]}" )
done동일한 파일을 여러 번 갖고 싶지 않은 경우 :
N=42
a=( * )
randf=()
for((i=0;i<N && ${#a[@]};++i)); do
((j=RANDOM%${#a[@]}))
randf+=( "${a[j]}" )
a=( "${a[@]:0:j}" "${a[@]:j+1}" )
done참고 . 이전 게시물에 대한 답변은 늦었지만 허용 된 답변은 외부 페이지로 연결되며세게 때리다연습하고 다른 답변은의 출력을 구문 분석하기 때문에 훨씬 좋지 않습니다 ls. 허용 된 답변에 대한 의견은 Lhunath의 훌륭한 답변을 나타내며, 이는 우수 사례를 분명히 보여 주지만 OP에 정확히 답변하지는 않습니다.
"{1..42}"후행을 남기는 부분이 마음에 들지 않았습니다 "1". 또한 $RANDOM15 비트이며이 방법은 32767 개가 넘는 파일에서 선택할 수 없습니다.
ls | shuf -n 10 # ten random files
ls. 예를 들어 파일 이름에 줄 바꿈이 포함되어 있으면 작동하지 않습니다.
ls"깨끗한"파일 이름을 제공한다고 보장하지 않으므로 파일 이름에 의존해서는 안됩니다. 이러한 문제가 드물거나 특이하다는 사실은 문제를 변경하지 않습니다. 특히 이것에 대한 더 나은 해결책이 있습니다.
ls디렉토리와 빈 줄을 포함 할 수 있습니다. find . -type f | shuf -n10대신에 비슷한 것을 제안 합니다.
ls 구문 분석5 을 피하면서 임의의 파일 을 선택하는 간단한 솔루션입니다 . 또한 공백, 개행 및 기타 특수 문자가 포함 된 파일과 함께 작동합니다.
shuf -ezn 5 * | xargs -0 -n1 echo
대체 echo당신이 당신의 파일을 실행하려는 명령.
read파싱과 같은 문제가 ls없습니까? 즉, 한 줄씩 읽으므로 이름에 개행 문자가있는 파일에는 작동하지 않습니다
Python을 설치 한 경우 (Python 2 또는 Python 3과 함께 작동) :
하나의 파일 (또는 임의의 명령에서 한 줄)을 선택하려면
ls -1 | python -c "import sys; import random; print(random.choice(sys.stdin.readlines()).rstrip())"
N파일 / 줄 을 선택하려면을 사용하십시오 ( N명령의 끝 부분에있는 숫자를 바꾸십시오)
ls -1 | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" N
이것은 @gniourf_gniourf의 늦은 답변에 대한 심지어 나중에 답변입니다. eval안전한 파일 이름 처리 를 피하기 위해 한 번.
그러나이 답변에서 사용하는 "잘 문서화되지 않은"기능을 풀려면 몇 분이 걸렸습니다. Bash 기술이 충분히 작동하여 즉시 작동 방식을 확인한 경우이 설명을 건너 뛰십시오. 그러나 나는 그것을 풀지 않았고 그것을 풀었을 때 나는 그것이 가치가 있다고 생각합니다.
기능 # 1 은 쉘 자체의 파일 글 로빙입니다. 멤버가 현재 디렉토리의 파일 인 a=(*)배열을 만듭니다 $a. Bash는 파일 이름의 모든 기묘함을 이해하므로 목록이 정확하고 이스케이프 등을 보장합니다 ls.에서 반환 된 텍스트 파일 이름을 올바르게 구문 분석 할 필요가 없습니다 .
기능 # 2 는 배열 에 대한 Bash 매개 변수 확장 입니다 . 이것은로 시작 하여 길이로 확장됩니다 .${#ARRAY[@]}$ARRAY
그런 다음 해당 확장을 사용하여 배열을 첨자 화합니다. 1과 N 사이의 난수를 찾는 표준 방법은 난수 modulo N의 값을 취하는 것입니다. 우리는 0과 배열의 길이 사이의 난수를 원합니다. 명확성을 위해 두 줄로 나눈 접근 방식은 다음과 같습니다.
LENGTH=${#ARRAY[@]}
RANDOM=${a[RANDOM%$LENGTH]}
그러나이 솔루션은 한 줄로 수행하여 불필요한 변수 할당을 제거합니다.
Feature # 3 은 Bash brace expansion 이지만, 완전히 이해하지는 못하지만 고백해야합니다. 중괄호 확장라는 25 개 파일 목록 생성하기 위해, 예를 들어, 사용 filename1.txt, filename2.txt등, : echo "filename"{1..25}".txt".
위의 서브 쉘 내부의 표현식 "${a[RANDOM%${#a[@]}]"{1..42}"}"은 해당 트릭을 사용하여 42 개의 개별 확장을 생성합니다. 브레이스 팽창 사이의 한 자리에 배치 ]하고, }제 I의 생각에 배열 첨자하고,하지만, 그렇다면 그것은 콜론이 선행되어야한다. (또한 배열의 임의의 지점에서 42 개의 연속 항목을 반환했을 것입니다. 이는 배열에서 임의의 42 개의 항목을 반환하는 것과 전혀 다릅니다.) 쉘이 확장을 42 번 실행하여 반환하는 것으로 생각합니다. 배열에서 42 개의 랜덤 아이템. (그러나 누군가가 더 자세히 설명 할 수 있다면 듣고 싶습니다.)
N이 42까지 하드 코딩되어야하는 이유는 변수 확장 전에 가새 확장이 발생하기 때문입니다.
마지막으로 디렉토리 계층 구조에 대해이 작업을 반복적으로 수행하려면 기능 # 4 가 있습니다.
shopt -s globstar
a=( ** )
A의이 회전 쉘 옵션 의 원인 **재귀 적으로 일치합니다. 이제 $a배열에는 전체 계층 구조의 모든 파일이 포함됩니다.
폴더에 더 많은 파일이 있으면 유닉스 stackexchange 에서 찾은 아래 파이프 명령을 사용할 수 있습니다 .
find /some/dir/ -type f -print0 | xargs -0 shuf -e -n 8 -z | xargs -0 cp -vt /target/dir/
여기에서 파일을 복사하고 싶었지만 파일을 이동하거나 다른 작업을 수행하려면 마지막으로 사용한 명령을 변경하십시오 cp.
이것은 MacOS에서 bash로 멋지게 플레이 할 수있는 유일한 스크립트입니다. 다음 두 링크에서 스 니펫을 결합하고 편집했습니다.
ls 명령 : 파일 당 한 줄씩 재귀 전체 경로 목록을 얻는 방법은 무엇입니까?
#!/bin/bash
# Reads a given directory and picks a random file.
# The directory you want to use. You could use "$1" instead if you
# wanted to parametrize it.
DIR="/path/to/"
# DIR="$1"
# Internal Field Separator set to newline, so file names with
# spaces do not break our script.
IFS='
'
if [[ -d "${DIR}" ]]
then
# Runs ls on the given dir, and dumps the output into a matrix,
# it uses the new lines character as a field delimiter, as explained above.
# file_matrix=($(ls -LR "${DIR}"))
file_matrix=($(ls -R $DIR | awk '; /:$/&&f{s=$0;f=0}; /:$/&&!f{sub(/:$/,"");s=$0;f=1;next}; NF&&f{ print s"/"$0 }'))
num_files=${#file_matrix[*]}
# This is the command you want to run on a random file.
# Change "ls -l" by anything you want, it's just an example.
ls -l "${file_matrix[$((RANDOM%num_files))]}"
fi
exit 0
MacOS에는 sort -R 및 shuf 명령이 없으므로 중복없이 모든 파일을 무작위로 추출 하고 여기서 찾지 못한 bash 전용 솔루션이 필요했습니다 . 이 솔루션은 gniourf_gniourf의 솔루션 # 4와 유사하지만 더 나은 주석을 추가하기를 바랍니다.
스크립트는 N이있는 카운터를 사용하거나 N이있는 gniourf_gniourf의 for 루프를 사용하여 N 샘플 후에 중지되도록 쉽게 수정해야합니다. $ RANDOM은 ~ 32000 파일로 제한되지만 대부분의 경우에 적용됩니다.
#!/bin/bash
array=(*) # this is the array of files to shuffle
# echo ${array[@]}
for dummy in "${array[@]}"; do # do loop length(array) times; once for each file
length=${#array[@]}
randomi=$(( $RANDOM % $length )) # select a random index
filename=${array[$randomi]}
echo "Processing: '$filename'" # do something with the file
unset -v "array[$randomi]" # set the element at index $randomi to NULL
array=("${array[@]}") # remove NULL elements introduced by unset; copy array
done
나는 이것을 사용한다 : 그것은 임시 파일을 사용하지만 일반 파일을 찾아서 반환 할 때까지 디렉토리에 깊숙이 들어간다.
# find for a quasi-random file in a directory tree:
# directory to start search from:
ROOT="/";
tmp=/tmp/mytempfile
TARGET="$ROOT"
FILE="";
n=
r=
while [ -e "$TARGET" ]; do
TARGET="$(readlink -f "${TARGET}/$FILE")" ;
if [ -d "$TARGET" ]; then
ls -1 "$TARGET" 2> /dev/null > $tmp || break;
n=$(cat $tmp | wc -l);
if [ $n != 0 ]; then
FILE=$(shuf -n 1 $tmp)
# or if you dont have/want to use shuf:
# r=$(($RANDOM % $n)) ;
# FILE=$(tail -n +$(( $r + 1 )) $tmp | head -n 1);
fi ;
else
if [ -f "$TARGET" ] ; then
rm -f $tmp
echo $TARGET
break;
else
# is not a regular file, restart:
TARGET="$ROOT"
FILE=""
fi
fi
done;
강 씨에게 약간의 펄 솔루션을 여기
에서 어떻습니까? 유닉스 명령 행이나 쉘 스크립트에서 텍스트 파일의 행을 어떻게 섞을 수 있습니까?
$ ls | perl -MList :: Util = shuffle -e '@lines = 셔플 (<>); @lines 인쇄 [0..4] '