从各种目录中随机选择一个文件并进行排序

时间:2014-10-16 04:22:18

标签: linux bash shell unix

我有很多文本文件分布在几个目录中。我想对所有文件进行排序并创建一个文件名列表(在文本文件中),但是以一种特殊的定义顺序。我最初的想法是从这些目录中随机选择第一个文件*1.txt。然后重复该过程(*2.txt, *3.txt, etc.),直到所有文件名都在列表中。我怎样才能在bash中实现这个目标?

基础:

从1个目录中随机选择文件:

shuf -n1 –e *

从1个目录中选择第一个文件:

ls | sort -n | head -1

示例:

UPDATED:文件结构/真实文件名格式(这只是几个文件,有几百个)

初始订单:

media/sf_linux_sandbox/papers/
|-- semester_1
|   |-- cs630-linux_research_paper-fname_lname-001.txt
|   |-- cs635-progamming_languages-fname_lname-002.txt
|   |-- cs645-java_programming_paper-fname_lname-003.txt
|   `-- cs900-computer_robotics_capstone-fname_lname-004.txt
|-- semester_2
|   |-- cs650-software_methodologies-fname_lname-001.txt
|   |-- cs675-nosql_db_research-fname_lname-002.txt
|   |-- cs700-artificial_intelligence_reasearch-fname_lname-003.txt
|   |-- cs800-algorithms_and_computational_complexity-fname_lname-004.txt
|   |-- cs825-database_systems_internals-fname_lname-005.txt
|   `-- cs850-computer_graphics-fname_lname-006.txt
|-- semester_3
    |-- cs725-web_programming_technologies-fname_lname-001.txt
    |-- cs750-data_programming-fname_lname-002.txt
    `-- cs775-hardware_software_interface_paper-fname_lname-003.txt

我想要生成的输出/结果(随机调整文件但保持数字顺序):

results.txt
/filepath/cs650-software_methodologies-fname_lname-001.txt
/filepath/s630-linux_research_paper-fname_lname-001.txt
/filepath/cs725-web_programming_technologies-fname_lname-001.txt
/filepath/cs635-progamming_languages-fname_lname-002.txt
/filepath/cs750-data_programming-fname_lname-002.txt
/filepath/cs675-nosql_db_research-fname_lname-002.txt
/filepath/cs645-java_programming_paper-fname_lname-003.txt
/filepath/cs775-hardware_software_interface_paper-fname_lname-003.txt
/filepath/cs700-artificial_intelligence_reasearch-fname_lname-003.txt
/filepath/cs900-computer_robotics_capstone-fname_lname-004.txt
/filepath/cs800-algorithms_and_computational_complexity-fname_lname-004.txt
/filepath/cs825-database_systems_internals-fname_lname-005.txt
/filepath/cs850-computer_graphics-fname_lname-006.txt

5 个答案:

答案 0 :(得分:4)

这会对源树中的所有文件进行洗牌,对数字部分进行部分排序,并使用稳定的排序,以便其他元素保持混洗。

$ target=~/tmp/shuf
$ destination=/filepath/
$ tree $target
~/tmp/shuf
`-- papers
    |-- semester_1
    |   |-- cs630-linux_research_paper-fname_lname-001.txt
    |   |-- cs635-progamming_languages-fname_lname-002.txt
    |   |-- cs645-java_programming_paper-fname_lname-003.txt
    |   `-- cs900-computer_robotics_capstone-fname_lname-004.txt
    |-- semester_2
    |   |-- cs650-software_methodologies-fname_lname-001.txt
    |   |-- cs675-nosql_db_research-fname_lname-002.txt
    |   |-- cs700-artificial_intelligence_reasearch-fname_lname-003.txt
    |   |-- cs800-algorithms_and_computational_complexity-fname_lname-004.txt
    |   |-- cs825-database_systems_internals-fname_lname-005.txt
    |   `-- cs850-computer_graphics-fname_lname-006.txt
    `-- semester_3
        |-- cs725-web_programming_technologies-fname_lname-001.txt
        |-- cs750-data_programming-fname_lname-002.txt
        `-- cs775-hardware_software_interface_paper-fname_lname-003.txt

4 directories, 13 files
$ find $target -type f -iname "*.txt" \
   | shuf \
   | awk -F- '{printf("%s:%s\n", $0, $NF)}' \
   | sort -t : -k 2 -s \
   | cut -d : -f 1 \
   | xargs -n1 basename \
   | sed "s,^,$destination,"
/filepath/cs725-web_programming_technologies-fname_lname-001.txt
/filepath/cs650-software_methodologies-fname_lname-001.txt
/filepath/cs630-linux_research_paper-fname_lname-001.txt
/filepath/cs635-progamming_languages-fname_lname-002.txt
/filepath/cs750-data_programming-fname_lname-002.txt
/filepath/cs675-nosql_db_research-fname_lname-002.txt
/filepath/cs775-hardware_software_interface_paper-fname_lname-003.txt
/filepath/cs700-artificial_intelligence_reasearch-fname_lname-003.txt
/filepath/cs645-java_programming_paper-fname_lname-003.txt
/filepath/cs900-computer_robotics_capstone-fname_lname-004.txt
/filepath/cs800-algorithms_and_computational_complexity-fname_lname-004.txt
/filepath/cs825-database_systems_internals-fname_lname-005.txt
/filepath/cs850-computer_graphics-fname_lname-006.txt

要将结果存储在名为filename的文件中,您可以重定向:

$ find $target -type f -iname "*.txt" \
   | shuf \
   | awk -F- '{printf("%s:%s\n", $0, $NF)}' \
   | sort -t : -k 2 -s \
   | cut -d : -f 1 \
   | xargs -n1 basename \
   | sed "s,^,$destination," \
   > filename

答案 1 :(得分:1)

我不确定我是否完全明白你的要求。您是否尝试根据文件名中的尾随数字进行数字排序?如果是这样,您将需要为您的文件名提供准确的规范,以便可以使用正确的正则表达式从中提取数字和排序...即。你的文件名总是[a-z] [1-9]还是有多个字符,特殊字符等?如果您能够提供正在使用的真实路径以及确切的预期输出,那么它可能会使事情变得更加容易。

回答问题'从各种目录中随机选择一个文件' ...这里有两个非常相似的方法来显示当前目录的每个子目录中一个随机文件的路径。

while IFS= read -r dir; do
    find "$dir" -maxdepth 1 -type f | shuf -n1
done < <(find -type d) > results.txt

或者...

shopt -s globstar
for dir in ./**/; do
    find "$dir" -maxdepth 1 -type f | shuf -n1
done > results.txt
shopt -u globstar

如果您想要每个随机文件的基本名称(而不是完整路径),可以使用以下内容替换内部find命令:

random="$(find "$dir" -maxdepth 1 -type f | shuf -n1)"
[[ -n $random ]] && echo "${random##*/}"

如果您只想选择随机txt文件,则只需将-name '*.txt'选项附加到内部find命令的末尾。

请注意,我使用了shuf命令,因为您在问题中提到过它,但它可能已经使用$ RANDOM轻松解决了。

答案 2 :(得分:1)

我尝试匹配您在撰写答案时在帖子中提供的输出。

#!/bin/bash
usage_exit () {
    echo "usage: $0 <target-directory>"
    exit 0
}

if [ $# != 1 ] ; then
    usage_exit
fi

# The pattern below searches files in the range 000 through 199.
# You can change the pattern to match your needs.
for n in {0..1}{0..9}{0..9}
    do find $1 -type f -name '*'$n'.txt' | shuf
done

答案 3 :(得分:1)

另一种替代方法,您可以将find-type d的结果存储在数组中。然后在数组的任何目录中找到最大数量的常规文件,将其用作((i=1; i<=max; i++))循环中的最大边界,在循环的每个主体中对数组进行混洗,然后遍历它复制{{1}在每个目录中存在文件,如果不存在则没有任何内容(即如果目录的文件少于$i'th)。

$i

示例

#!/bin/bash

#shuffle function taken from http://mywiki.wooledge.org/BashFAQ/026
shuffle() {
   local i tmp size max rand

   # $RANDOM % (i+1) is biased because of the limited range of $RANDOM
   # Compensate by using a range which is a multiple of the array size.
   size=${#array[*]}
   max=$(( 32768 / size * size ))

   for ((i=size-1; i>0; i--)); do
      while (( (rand=$RANDOM) >= max )); do :; done
      rand=$(( rand % (i+1) ))
      tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
   done
}

destination=/filepath
max=0
shopt -s nullglob dotglob
while IFS= read -d $'\0' -r dir ; do
  array+=("$dir")
  count=$(ls -F "$dir" | egrep -v "^*[/*]$" | wc -l)
  ((count>max)) && max=$count
done < <(find . -mindepth 1 -type d -print0)

for ((i=1; i<=max; i++)); do
  shuffle
  for dir in "${array[@]}"; do
    file=$(find "$dir" -maxdepth 1 -type f -iname '*.txt' | sort -n | awk "NR==$i")
    [[ -n $file ]] && echo "$destination/$file" 
  done
done

答案 4 :(得分:-1)

我认为这可能有用:

dir='some/directory'
file=`/bin/ls -1 "$dir" | sort --random-sort | head -1`
path=`readlink --canonicalize "$dir/$file"` # Converts to full path
echo "The randomly-selected file is: $path"

请考虑查看以下question

希望它有所帮助。

Clemencio Morales Lucas。